Completed
Pull Request — master (#9345)
by Björn
163:05 queued 139:41
created
apps/dav/lib/Comments/CommentNode.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -93,7 +93,7 @@
 block discarded – undo
93 93
 	/**
94 94
 	 * returns a list of all possible property names
95 95
 	 *
96
-	 * @return array
96
+	 * @return string[]
97 97
 	 */
98 98
 	static public function getPropertyNames() {
99 99
 		return [
Please login to merge, or discard this patch.
Indentation   +241 added lines, -241 removed lines patch added patch discarded remove patch
@@ -36,270 +36,270 @@
 block discarded – undo
36 36
 use Sabre\DAV\PropPatch;
37 37
 
38 38
 class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
39
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
39
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
40 40
 
41
-	const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}isUnread';
42
-	const PROPERTY_NAME_MESSAGE = '{http://owncloud.org/ns}message';
43
-	const PROPERTY_NAME_ACTOR_DISPLAYNAME = '{http://owncloud.org/ns}actorDisplayName';
44
-	const PROPERTY_NAME_MENTIONS = '{http://owncloud.org/ns}mentions';
45
-	const PROPERTY_NAME_MENTION = '{http://owncloud.org/ns}mention';
46
-	const PROPERTY_NAME_MENTION_TYPE = '{http://owncloud.org/ns}mentionType';
47
-	const PROPERTY_NAME_MENTION_ID = '{http://owncloud.org/ns}mentionId';
48
-	const PROPERTY_NAME_MENTION_DISPLAYNAME = '{http://owncloud.org/ns}mentionDisplayName';
41
+    const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}isUnread';
42
+    const PROPERTY_NAME_MESSAGE = '{http://owncloud.org/ns}message';
43
+    const PROPERTY_NAME_ACTOR_DISPLAYNAME = '{http://owncloud.org/ns}actorDisplayName';
44
+    const PROPERTY_NAME_MENTIONS = '{http://owncloud.org/ns}mentions';
45
+    const PROPERTY_NAME_MENTION = '{http://owncloud.org/ns}mention';
46
+    const PROPERTY_NAME_MENTION_TYPE = '{http://owncloud.org/ns}mentionType';
47
+    const PROPERTY_NAME_MENTION_ID = '{http://owncloud.org/ns}mentionId';
48
+    const PROPERTY_NAME_MENTION_DISPLAYNAME = '{http://owncloud.org/ns}mentionDisplayName';
49 49
 
50
-	/** @var  IComment */
51
-	public $comment;
50
+    /** @var  IComment */
51
+    public $comment;
52 52
 
53
-	/** @var ICommentsManager */
54
-	protected $commentsManager;
53
+    /** @var ICommentsManager */
54
+    protected $commentsManager;
55 55
 
56
-	/** @var  ILogger */
57
-	protected $logger;
56
+    /** @var  ILogger */
57
+    protected $logger;
58 58
 
59
-	/** @var array list of properties with key being their name and value their setter */
60
-	protected $properties = [];
59
+    /** @var array list of properties with key being their name and value their setter */
60
+    protected $properties = [];
61 61
 
62
-	/** @var IUserManager */
63
-	protected $userManager;
62
+    /** @var IUserManager */
63
+    protected $userManager;
64 64
 
65
-	/** @var IUserSession */
66
-	protected $userSession;
65
+    /** @var IUserSession */
66
+    protected $userSession;
67 67
 
68
-	/**
69
-	 * CommentNode constructor.
70
-	 *
71
-	 * @param ICommentsManager $commentsManager
72
-	 * @param IComment $comment
73
-	 * @param IUserManager $userManager
74
-	 * @param IUserSession $userSession
75
-	 * @param ILogger $logger
76
-	 */
77
-	public function __construct(
78
-		ICommentsManager $commentsManager,
79
-		IComment $comment,
80
-		IUserManager $userManager,
81
-		IUserSession $userSession,
82
-		ILogger $logger
83
-	) {
84
-		$this->commentsManager = $commentsManager;
85
-		$this->comment = $comment;
86
-		$this->logger = $logger;
68
+    /**
69
+     * CommentNode constructor.
70
+     *
71
+     * @param ICommentsManager $commentsManager
72
+     * @param IComment $comment
73
+     * @param IUserManager $userManager
74
+     * @param IUserSession $userSession
75
+     * @param ILogger $logger
76
+     */
77
+    public function __construct(
78
+        ICommentsManager $commentsManager,
79
+        IComment $comment,
80
+        IUserManager $userManager,
81
+        IUserSession $userSession,
82
+        ILogger $logger
83
+    ) {
84
+        $this->commentsManager = $commentsManager;
85
+        $this->comment = $comment;
86
+        $this->logger = $logger;
87 87
 
88
-		$methods = get_class_methods($this->comment);
89
-		$methods = array_filter($methods, function($name){
90
-			return strpos($name, 'get') === 0;
91
-		});
92
-		foreach($methods as $getter) {
93
-			if($getter === 'getMentions') {
94
-				continue;	// special treatment
95
-			}
96
-			$name = '{'.self::NS_OWNCLOUD.'}' . lcfirst(substr($getter, 3));
97
-			$this->properties[$name] = $getter;
98
-		}
99
-		$this->userManager = $userManager;
100
-		$this->userSession = $userSession;
101
-	}
88
+        $methods = get_class_methods($this->comment);
89
+        $methods = array_filter($methods, function($name){
90
+            return strpos($name, 'get') === 0;
91
+        });
92
+        foreach($methods as $getter) {
93
+            if($getter === 'getMentions') {
94
+                continue;	// special treatment
95
+            }
96
+            $name = '{'.self::NS_OWNCLOUD.'}' . lcfirst(substr($getter, 3));
97
+            $this->properties[$name] = $getter;
98
+        }
99
+        $this->userManager = $userManager;
100
+        $this->userSession = $userSession;
101
+    }
102 102
 
103
-	/**
104
-	 * returns a list of all possible property names
105
-	 *
106
-	 * @return array
107
-	 */
108
-	static public function getPropertyNames() {
109
-		return [
110
-			'{http://owncloud.org/ns}id',
111
-			'{http://owncloud.org/ns}parentId',
112
-			'{http://owncloud.org/ns}topmostParentId',
113
-			'{http://owncloud.org/ns}childrenCount',
114
-			'{http://owncloud.org/ns}verb',
115
-			'{http://owncloud.org/ns}actorType',
116
-			'{http://owncloud.org/ns}actorId',
117
-			'{http://owncloud.org/ns}creationDateTime',
118
-			'{http://owncloud.org/ns}latestChildDateTime',
119
-			'{http://owncloud.org/ns}objectType',
120
-			'{http://owncloud.org/ns}objectId',
121
-			// re-used property names are defined as constants
122
-			self::PROPERTY_NAME_MESSAGE,
123
-			self::PROPERTY_NAME_ACTOR_DISPLAYNAME,
124
-			self::PROPERTY_NAME_UNREAD,
125
-			self::PROPERTY_NAME_MENTIONS,
126
-			self::PROPERTY_NAME_MENTION,
127
-			self::PROPERTY_NAME_MENTION_TYPE,
128
-			self::PROPERTY_NAME_MENTION_ID,
129
-			self::PROPERTY_NAME_MENTION_DISPLAYNAME,
130
-		];
131
-	}
103
+    /**
104
+     * returns a list of all possible property names
105
+     *
106
+     * @return array
107
+     */
108
+    static public function getPropertyNames() {
109
+        return [
110
+            '{http://owncloud.org/ns}id',
111
+            '{http://owncloud.org/ns}parentId',
112
+            '{http://owncloud.org/ns}topmostParentId',
113
+            '{http://owncloud.org/ns}childrenCount',
114
+            '{http://owncloud.org/ns}verb',
115
+            '{http://owncloud.org/ns}actorType',
116
+            '{http://owncloud.org/ns}actorId',
117
+            '{http://owncloud.org/ns}creationDateTime',
118
+            '{http://owncloud.org/ns}latestChildDateTime',
119
+            '{http://owncloud.org/ns}objectType',
120
+            '{http://owncloud.org/ns}objectId',
121
+            // re-used property names are defined as constants
122
+            self::PROPERTY_NAME_MESSAGE,
123
+            self::PROPERTY_NAME_ACTOR_DISPLAYNAME,
124
+            self::PROPERTY_NAME_UNREAD,
125
+            self::PROPERTY_NAME_MENTIONS,
126
+            self::PROPERTY_NAME_MENTION,
127
+            self::PROPERTY_NAME_MENTION_TYPE,
128
+            self::PROPERTY_NAME_MENTION_ID,
129
+            self::PROPERTY_NAME_MENTION_DISPLAYNAME,
130
+        ];
131
+    }
132 132
 
133
-	protected function checkWriteAccessOnComment() {
134
-		$user = $this->userSession->getUser();
135
-		if(    $this->comment->getActorType() !== 'users'
136
-			|| is_null($user)
137
-			|| $this->comment->getActorId() !== $user->getUID()
138
-		) {
139
-			throw new Forbidden('Only authors are allowed to edit their comment.');
140
-		}
141
-	}
133
+    protected function checkWriteAccessOnComment() {
134
+        $user = $this->userSession->getUser();
135
+        if(    $this->comment->getActorType() !== 'users'
136
+            || is_null($user)
137
+            || $this->comment->getActorId() !== $user->getUID()
138
+        ) {
139
+            throw new Forbidden('Only authors are allowed to edit their comment.');
140
+        }
141
+    }
142 142
 
143
-	/**
144
-	 * Deleted the current node
145
-	 *
146
-	 * @return void
147
-	 */
148
-	function delete() {
149
-		$this->checkWriteAccessOnComment();
150
-		$this->commentsManager->delete($this->comment->getId());
151
-	}
143
+    /**
144
+     * Deleted the current node
145
+     *
146
+     * @return void
147
+     */
148
+    function delete() {
149
+        $this->checkWriteAccessOnComment();
150
+        $this->commentsManager->delete($this->comment->getId());
151
+    }
152 152
 
153
-	/**
154
-	 * Returns the name of the node.
155
-	 *
156
-	 * This is used to generate the url.
157
-	 *
158
-	 * @return string
159
-	 */
160
-	function getName() {
161
-		return $this->comment->getId();
162
-	}
153
+    /**
154
+     * Returns the name of the node.
155
+     *
156
+     * This is used to generate the url.
157
+     *
158
+     * @return string
159
+     */
160
+    function getName() {
161
+        return $this->comment->getId();
162
+    }
163 163
 
164
-	/**
165
-	 * Renames the node
166
-	 *
167
-	 * @param string $name The new name
168
-	 * @throws MethodNotAllowed
169
-	 */
170
-	function setName($name) {
171
-		throw new MethodNotAllowed();
172
-	}
164
+    /**
165
+     * Renames the node
166
+     *
167
+     * @param string $name The new name
168
+     * @throws MethodNotAllowed
169
+     */
170
+    function setName($name) {
171
+        throw new MethodNotAllowed();
172
+    }
173 173
 
174
-	/**
175
-	 * Returns the last modification time, as a unix timestamp
176
-	 *
177
-	 * @return int
178
-	 */
179
-	function getLastModified() {
180
-		return null;
181
-	}
174
+    /**
175
+     * Returns the last modification time, as a unix timestamp
176
+     *
177
+     * @return int
178
+     */
179
+    function getLastModified() {
180
+        return null;
181
+    }
182 182
 
183
-	/**
184
-	 * update the comment's message
185
-	 *
186
-	 * @param $propertyValue
187
-	 * @return bool
188
-	 * @throws BadRequest
189
-	 * @throws \Exception
190
-	 */
191
-	public function updateComment($propertyValue) {
192
-		$this->checkWriteAccessOnComment();
193
-		try {
194
-			$this->comment->setMessage($propertyValue);
195
-			$this->commentsManager->save($this->comment);
196
-			return true;
197
-		} catch (\Exception $e) {
198
-			$this->logger->logException($e, ['app' => 'dav/comments']);
199
-			if($e instanceof MessageTooLongException) {
200
-				$msg = 'Message exceeds allowed character limit of ';
201
-				throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e);
202
-			}
203
-			throw $e;
204
-		}
205
-	}
183
+    /**
184
+     * update the comment's message
185
+     *
186
+     * @param $propertyValue
187
+     * @return bool
188
+     * @throws BadRequest
189
+     * @throws \Exception
190
+     */
191
+    public function updateComment($propertyValue) {
192
+        $this->checkWriteAccessOnComment();
193
+        try {
194
+            $this->comment->setMessage($propertyValue);
195
+            $this->commentsManager->save($this->comment);
196
+            return true;
197
+        } catch (\Exception $e) {
198
+            $this->logger->logException($e, ['app' => 'dav/comments']);
199
+            if($e instanceof MessageTooLongException) {
200
+                $msg = 'Message exceeds allowed character limit of ';
201
+                throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e);
202
+            }
203
+            throw $e;
204
+        }
205
+    }
206 206
 
207
-	/**
208
-	 * Updates properties on this node.
209
-	 *
210
-	 * This method received a PropPatch object, which contains all the
211
-	 * information about the update.
212
-	 *
213
-	 * To update specific properties, call the 'handle' method on this object.
214
-	 * Read the PropPatch documentation for more information.
215
-	 *
216
-	 * @param PropPatch $propPatch
217
-	 * @return void
218
-	 */
219
-	function propPatch(PropPatch $propPatch) {
220
-		// other properties than 'message' are read only
221
-		$propPatch->handle(self::PROPERTY_NAME_MESSAGE, [$this, 'updateComment']);
222
-	}
207
+    /**
208
+     * Updates properties on this node.
209
+     *
210
+     * This method received a PropPatch object, which contains all the
211
+     * information about the update.
212
+     *
213
+     * To update specific properties, call the 'handle' method on this object.
214
+     * Read the PropPatch documentation for more information.
215
+     *
216
+     * @param PropPatch $propPatch
217
+     * @return void
218
+     */
219
+    function propPatch(PropPatch $propPatch) {
220
+        // other properties than 'message' are read only
221
+        $propPatch->handle(self::PROPERTY_NAME_MESSAGE, [$this, 'updateComment']);
222
+    }
223 223
 
224
-	/**
225
-	 * Returns a list of properties for this nodes.
226
-	 *
227
-	 * The properties list is a list of propertynames the client requested,
228
-	 * encoded in clark-notation {xmlnamespace}tagname
229
-	 *
230
-	 * If the array is empty, it means 'all properties' were requested.
231
-	 *
232
-	 * Note that it's fine to liberally give properties back, instead of
233
-	 * conforming to the list of requested properties.
234
-	 * The Server class will filter out the extra.
235
-	 *
236
-	 * @param array $properties
237
-	 * @return array
238
-	 */
239
-	function getProperties($properties) {
240
-		$properties = array_keys($this->properties);
224
+    /**
225
+     * Returns a list of properties for this nodes.
226
+     *
227
+     * The properties list is a list of propertynames the client requested,
228
+     * encoded in clark-notation {xmlnamespace}tagname
229
+     *
230
+     * If the array is empty, it means 'all properties' were requested.
231
+     *
232
+     * Note that it's fine to liberally give properties back, instead of
233
+     * conforming to the list of requested properties.
234
+     * The Server class will filter out the extra.
235
+     *
236
+     * @param array $properties
237
+     * @return array
238
+     */
239
+    function getProperties($properties) {
240
+        $properties = array_keys($this->properties);
241 241
 
242
-		$result = [];
243
-		foreach($properties as $property) {
244
-			$getter = $this->properties[$property];
245
-			if(method_exists($this->comment, $getter)) {
246
-				$result[$property] = $this->comment->$getter();
247
-			}
248
-		}
242
+        $result = [];
243
+        foreach($properties as $property) {
244
+            $getter = $this->properties[$property];
245
+            if(method_exists($this->comment, $getter)) {
246
+                $result[$property] = $this->comment->$getter();
247
+            }
248
+        }
249 249
 
250
-		if($this->comment->getActorType() === 'users') {
251
-			$user = $this->userManager->get($this->comment->getActorId());
252
-			$displayName = is_null($user) ? null : $user->getDisplayName();
253
-			$result[self::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName;
254
-		}
250
+        if($this->comment->getActorType() === 'users') {
251
+            $user = $this->userManager->get($this->comment->getActorId());
252
+            $displayName = is_null($user) ? null : $user->getDisplayName();
253
+            $result[self::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName;
254
+        }
255 255
 
256
-		$result[self::PROPERTY_NAME_MENTIONS] = $this->composeMentionsPropertyValue();
256
+        $result[self::PROPERTY_NAME_MENTIONS] = $this->composeMentionsPropertyValue();
257 257
 
258
-		$unread = null;
259
-		$user =  $this->userSession->getUser();
260
-		if(!is_null($user)) {
261
-			$readUntil = $this->commentsManager->getReadMark(
262
-				$this->comment->getObjectType(),
263
-				$this->comment->getObjectId(),
264
-				$user
265
-			);
266
-			if(is_null($readUntil)) {
267
-				$unread = 'true';
268
-			} else {
269
-				$unread = $this->comment->getCreationDateTime() > $readUntil;
270
-				// re-format for output
271
-				$unread = $unread ? 'true' : 'false';
272
-			}
273
-		}
274
-		$result[self::PROPERTY_NAME_UNREAD] = $unread;
258
+        $unread = null;
259
+        $user =  $this->userSession->getUser();
260
+        if(!is_null($user)) {
261
+            $readUntil = $this->commentsManager->getReadMark(
262
+                $this->comment->getObjectType(),
263
+                $this->comment->getObjectId(),
264
+                $user
265
+            );
266
+            if(is_null($readUntil)) {
267
+                $unread = 'true';
268
+            } else {
269
+                $unread = $this->comment->getCreationDateTime() > $readUntil;
270
+                // re-format for output
271
+                $unread = $unread ? 'true' : 'false';
272
+            }
273
+        }
274
+        $result[self::PROPERTY_NAME_UNREAD] = $unread;
275 275
 
276
-		return $result;
277
-	}
276
+        return $result;
277
+    }
278 278
 
279
-	/**
280
-	 * transforms a mentions array as returned from IComment->getMentions to an
281
-	 * array with DAV-compatible structure that can be assigned to the
282
-	 * PROPERTY_NAME_MENTION property.
283
-	 *
284
-	 * @return array
285
-	 */
286
-	protected function composeMentionsPropertyValue() {
287
-		return array_map(function($mention) {
288
-			try {
289
-				$displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']);
290
-			} catch (\OutOfBoundsException $e) {
291
-				$this->logger->logException($e);
292
-				// No displayname, upon client's discretion what to display.
293
-				$displayName = '';
294
-			}
279
+    /**
280
+     * transforms a mentions array as returned from IComment->getMentions to an
281
+     * array with DAV-compatible structure that can be assigned to the
282
+     * PROPERTY_NAME_MENTION property.
283
+     *
284
+     * @return array
285
+     */
286
+    protected function composeMentionsPropertyValue() {
287
+        return array_map(function($mention) {
288
+            try {
289
+                $displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']);
290
+            } catch (\OutOfBoundsException $e) {
291
+                $this->logger->logException($e);
292
+                // No displayname, upon client's discretion what to display.
293
+                $displayName = '';
294
+            }
295 295
 
296
-			return [
297
-				self::PROPERTY_NAME_MENTION => [
298
-					self::PROPERTY_NAME_MENTION_TYPE        => $mention['type'],
299
-					self::PROPERTY_NAME_MENTION_ID          => $mention['id'],
300
-					self::PROPERTY_NAME_MENTION_DISPLAYNAME => $displayName,
301
-				]
302
-			];
303
-		}, $this->comment->getMentions());
304
-	}
296
+            return [
297
+                self::PROPERTY_NAME_MENTION => [
298
+                    self::PROPERTY_NAME_MENTION_TYPE        => $mention['type'],
299
+                    self::PROPERTY_NAME_MENTION_ID          => $mention['id'],
300
+                    self::PROPERTY_NAME_MENTION_DISPLAYNAME => $displayName,
301
+                ]
302
+            ];
303
+        }, $this->comment->getMentions());
304
+    }
305 305
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -86,14 +86,14 @@  discard block
 block discarded – undo
86 86
 		$this->logger = $logger;
87 87
 
88 88
 		$methods = get_class_methods($this->comment);
89
-		$methods = array_filter($methods, function($name){
89
+		$methods = array_filter($methods, function($name) {
90 90
 			return strpos($name, 'get') === 0;
91 91
 		});
92
-		foreach($methods as $getter) {
93
-			if($getter === 'getMentions') {
94
-				continue;	// special treatment
92
+		foreach ($methods as $getter) {
93
+			if ($getter === 'getMentions') {
94
+				continue; // special treatment
95 95
 			}
96
-			$name = '{'.self::NS_OWNCLOUD.'}' . lcfirst(substr($getter, 3));
96
+			$name = '{'.self::NS_OWNCLOUD.'}'.lcfirst(substr($getter, 3));
97 97
 			$this->properties[$name] = $getter;
98 98
 		}
99 99
 		$this->userManager = $userManager;
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 
133 133
 	protected function checkWriteAccessOnComment() {
134 134
 		$user = $this->userSession->getUser();
135
-		if(    $this->comment->getActorType() !== 'users'
135
+		if ($this->comment->getActorType() !== 'users'
136 136
 			|| is_null($user)
137 137
 			|| $this->comment->getActorId() !== $user->getUID()
138 138
 		) {
@@ -196,9 +196,9 @@  discard block
 block discarded – undo
196 196
 			return true;
197 197
 		} catch (\Exception $e) {
198 198
 			$this->logger->logException($e, ['app' => 'dav/comments']);
199
-			if($e instanceof MessageTooLongException) {
199
+			if ($e instanceof MessageTooLongException) {
200 200
 				$msg = 'Message exceeds allowed character limit of ';
201
-				throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e);
201
+				throw new BadRequest($msg.IComment::MAX_MESSAGE_LENGTH, 0, $e);
202 202
 			}
203 203
 			throw $e;
204 204
 		}
@@ -240,14 +240,14 @@  discard block
 block discarded – undo
240 240
 		$properties = array_keys($this->properties);
241 241
 
242 242
 		$result = [];
243
-		foreach($properties as $property) {
243
+		foreach ($properties as $property) {
244 244
 			$getter = $this->properties[$property];
245
-			if(method_exists($this->comment, $getter)) {
245
+			if (method_exists($this->comment, $getter)) {
246 246
 				$result[$property] = $this->comment->$getter();
247 247
 			}
248 248
 		}
249 249
 
250
-		if($this->comment->getActorType() === 'users') {
250
+		if ($this->comment->getActorType() === 'users') {
251 251
 			$user = $this->userManager->get($this->comment->getActorId());
252 252
 			$displayName = is_null($user) ? null : $user->getDisplayName();
253 253
 			$result[self::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName;
@@ -256,14 +256,14 @@  discard block
 block discarded – undo
256 256
 		$result[self::PROPERTY_NAME_MENTIONS] = $this->composeMentionsPropertyValue();
257 257
 
258 258
 		$unread = null;
259
-		$user =  $this->userSession->getUser();
260
-		if(!is_null($user)) {
259
+		$user = $this->userSession->getUser();
260
+		if (!is_null($user)) {
261 261
 			$readUntil = $this->commentsManager->getReadMark(
262 262
 				$this->comment->getObjectType(),
263 263
 				$this->comment->getObjectId(),
264 264
 				$user
265 265
 			);
266
-			if(is_null($readUntil)) {
266
+			if (is_null($readUntil)) {
267 267
 				$unread = 'true';
268 268
 			} else {
269 269
 				$unread = $this->comment->getCreationDateTime() > $readUntil;
Please login to merge, or discard this patch.
apps/dav/lib/Comments/CommentsPlugin.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -138,7 +138,7 @@
 block discarded – undo
138 138
 	 * This will be used in the {DAV:}supported-report-set property.
139 139
 	 *
140 140
 	 * @param string $uri
141
-	 * @return array
141
+	 * @return string[]
142 142
 	 */
143 143
 	public function getSupportedReportSet($uri) {
144 144
 		return [self::REPORT_NAME];
Please login to merge, or discard this patch.
Indentation   +207 added lines, -207 removed lines patch added patch discarded remove patch
@@ -43,213 +43,213 @@
 block discarded – undo
43 43
  * Sabre plugin to handle comments:
44 44
  */
45 45
 class CommentsPlugin extends ServerPlugin {
46
-	// namespace
47
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
48
-
49
-	const REPORT_NAME            = '{http://owncloud.org/ns}filter-comments';
50
-	const REPORT_PARAM_LIMIT     = '{http://owncloud.org/ns}limit';
51
-	const REPORT_PARAM_OFFSET    = '{http://owncloud.org/ns}offset';
52
-	const REPORT_PARAM_TIMESTAMP = '{http://owncloud.org/ns}datetime';
53
-
54
-	/** @var ICommentsManager  */
55
-	protected $commentsManager;
56
-
57
-	/** @var \Sabre\DAV\Server $server */
58
-	private $server;
59
-
60
-	/** @var  \OCP\IUserSession */
61
-	protected $userSession;
62
-
63
-	/**
64
-	 * Comments plugin
65
-	 *
66
-	 * @param ICommentsManager $commentsManager
67
-	 * @param IUserSession $userSession
68
-	 */
69
-	public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) {
70
-		$this->commentsManager = $commentsManager;
71
-		$this->userSession = $userSession;
72
-	}
73
-
74
-	/**
75
-	 * This initializes the plugin.
76
-	 *
77
-	 * This function is called by Sabre\DAV\Server, after
78
-	 * addPlugin is called.
79
-	 *
80
-	 * This method should set up the required event subscriptions.
81
-	 *
82
-	 * @param Server $server
83
-	 * @return void
84
-	 */
85
-	function initialize(Server $server) {
86
-		$this->server = $server;
87
-		if(strpos($this->server->getRequestUri(), 'comments/') !== 0) {
88
-			return;
89
-		}
90
-
91
-		$this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
92
-
93
-		$this->server->xml->classMap['DateTime'] = function(Writer $writer, \DateTime $value) {
94
-			$writer->write(\Sabre\HTTP\toDate($value));
95
-		};
96
-
97
-		$this->server->on('report', [$this, 'onReport']);
98
-		$this->server->on('method:POST', [$this, 'httpPost']);
99
-	}
100
-
101
-	/**
102
-	 * POST operation on Comments collections
103
-	 *
104
-	 * @param RequestInterface $request request object
105
-	 * @param ResponseInterface $response response object
106
-	 * @return null|false
107
-	 */
108
-	public function httpPost(RequestInterface $request, ResponseInterface $response) {
109
-		$path = $request->getPath();
110
-		$node = $this->server->tree->getNodeForPath($path);
111
-		if (!$node instanceof EntityCollection) {
112
-			return null;
113
-		}
114
-
115
-		$data = $request->getBodyAsString();
116
-		$comment = $this->createComment(
117
-			$node->getName(),
118
-			$node->getId(),
119
-			$data,
120
-			$request->getHeader('Content-Type')
121
-		);
122
-
123
-		// update read marker for the current user/poster to avoid
124
-		// having their own comments marked as unread
125
-		$node->setReadMarker(null);
126
-
127
-		$url = rtrim($request->getUrl(), '/') . '/' . urlencode($comment->getId());
128
-
129
-		$response->setHeader('Content-Location', $url);
130
-
131
-		// created
132
-		$response->setStatus(201);
133
-		return false;
134
-	}
135
-
136
-	/**
137
-	 * Returns a list of reports this plugin supports.
138
-	 *
139
-	 * This will be used in the {DAV:}supported-report-set property.
140
-	 *
141
-	 * @param string $uri
142
-	 * @return array
143
-	 */
144
-	public function getSupportedReportSet($uri) {
145
-		return [self::REPORT_NAME];
146
-	}
147
-
148
-	/**
149
-	 * REPORT operations to look for comments
150
-	 *
151
-	 * @param string $reportName
152
-	 * @param array $report
153
-	 * @param string $uri
154
-	 * @return bool
155
-	 * @throws NotFound
156
-	 * @throws ReportNotSupported
157
-	 */
158
-	public function onReport($reportName, $report, $uri) {
159
-		$node = $this->server->tree->getNodeForPath($uri);
160
-		if(!$node instanceof EntityCollection || $reportName !== self::REPORT_NAME) {
161
-			throw new ReportNotSupported();
162
-		}
163
-		$args = ['limit' => 0, 'offset' => 0, 'datetime' => null];
164
-		$acceptableParameters = [
165
-			$this::REPORT_PARAM_LIMIT,
166
-			$this::REPORT_PARAM_OFFSET,
167
-			$this::REPORT_PARAM_TIMESTAMP
168
-		];
169
-		$ns = '{' . $this::NS_OWNCLOUD . '}';
170
-		foreach($report as $parameter) {
171
-			if(!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) {
172
-				continue;
173
-			}
174
-			$args[str_replace($ns, '', $parameter['name'])] = $parameter['value'];
175
-		}
176
-
177
-		if(!is_null($args['datetime'])) {
178
-			$args['datetime'] = new \DateTime($args['datetime']);
179
-		}
180
-
181
-		$results = $node->findChildren($args['limit'], $args['offset'], $args['datetime']);
182
-
183
-		$responses = [];
184
-		foreach($results as $node) {
185
-			$nodePath = $this->server->getRequestUri() . '/' . $node->comment->getId();
186
-			$resultSet = $this->server->getPropertiesForPath($nodePath, CommentNode::getPropertyNames());
187
-			if(isset($resultSet[0]) && isset($resultSet[0][200])) {
188
-				$responses[] = new Response(
189
-					$this->server->getBaseUri() . $nodePath,
190
-					[200 => $resultSet[0][200]],
191
-					200
192
-				);
193
-			}
194
-
195
-		}
196
-
197
-		$xml = $this->server->xml->write(
198
-			'{DAV:}multistatus',
199
-			new MultiStatus($responses)
200
-		);
201
-
202
-		$this->server->httpResponse->setStatus(207);
203
-		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
204
-		$this->server->httpResponse->setBody($xml);
205
-
206
-		return false;
207
-	}
208
-
209
-	/**
210
-	 * Creates a new comment
211
-	 *
212
-	 * @param string $objectType e.g. "files"
213
-	 * @param string $objectId e.g. the file id
214
-	 * @param string $data JSON encoded string containing the properties of the tag to create
215
-	 * @param string $contentType content type of the data
216
-	 * @return IComment newly created comment
217
-	 *
218
-	 * @throws BadRequest if a field was missing
219
-	 * @throws UnsupportedMediaType if the content type is not supported
220
-	 */
221
-	private function createComment($objectType, $objectId, $data, $contentType = 'application/json') {
222
-		if (explode(';', $contentType)[0] === 'application/json') {
223
-			$data = json_decode($data, true);
224
-		} else {
225
-			throw new UnsupportedMediaType();
226
-		}
227
-
228
-		$actorType = $data['actorType'];
229
-		$actorId = null;
230
-		if($actorType === 'users') {
231
-			$user = $this->userSession->getUser();
232
-			if(!is_null($user)) {
233
-				$actorId = $user->getUID();
234
-			}
235
-		}
236
-		if(is_null($actorId)) {
237
-			throw new BadRequest('Invalid actor "' .  $actorType .'"');
238
-		}
239
-
240
-		try {
241
-			$comment = $this->commentsManager->create($actorType, $actorId, $objectType, $objectId);
242
-			$comment->setMessage($data['message']);
243
-			$comment->setVerb($data['verb']);
244
-			$this->commentsManager->save($comment);
245
-			return $comment;
246
-		} catch (\InvalidArgumentException $e) {
247
-			throw new BadRequest('Invalid input values', 0, $e);
248
-		} catch (\OCP\Comments\MessageTooLongException $e) {
249
-			$msg = 'Message exceeds allowed character limit of ';
250
-			throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0,	$e);
251
-		}
252
-	}
46
+    // namespace
47
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
48
+
49
+    const REPORT_NAME            = '{http://owncloud.org/ns}filter-comments';
50
+    const REPORT_PARAM_LIMIT     = '{http://owncloud.org/ns}limit';
51
+    const REPORT_PARAM_OFFSET    = '{http://owncloud.org/ns}offset';
52
+    const REPORT_PARAM_TIMESTAMP = '{http://owncloud.org/ns}datetime';
53
+
54
+    /** @var ICommentsManager  */
55
+    protected $commentsManager;
56
+
57
+    /** @var \Sabre\DAV\Server $server */
58
+    private $server;
59
+
60
+    /** @var  \OCP\IUserSession */
61
+    protected $userSession;
62
+
63
+    /**
64
+     * Comments plugin
65
+     *
66
+     * @param ICommentsManager $commentsManager
67
+     * @param IUserSession $userSession
68
+     */
69
+    public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) {
70
+        $this->commentsManager = $commentsManager;
71
+        $this->userSession = $userSession;
72
+    }
73
+
74
+    /**
75
+     * This initializes the plugin.
76
+     *
77
+     * This function is called by Sabre\DAV\Server, after
78
+     * addPlugin is called.
79
+     *
80
+     * This method should set up the required event subscriptions.
81
+     *
82
+     * @param Server $server
83
+     * @return void
84
+     */
85
+    function initialize(Server $server) {
86
+        $this->server = $server;
87
+        if(strpos($this->server->getRequestUri(), 'comments/') !== 0) {
88
+            return;
89
+        }
90
+
91
+        $this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
92
+
93
+        $this->server->xml->classMap['DateTime'] = function(Writer $writer, \DateTime $value) {
94
+            $writer->write(\Sabre\HTTP\toDate($value));
95
+        };
96
+
97
+        $this->server->on('report', [$this, 'onReport']);
98
+        $this->server->on('method:POST', [$this, 'httpPost']);
99
+    }
100
+
101
+    /**
102
+     * POST operation on Comments collections
103
+     *
104
+     * @param RequestInterface $request request object
105
+     * @param ResponseInterface $response response object
106
+     * @return null|false
107
+     */
108
+    public function httpPost(RequestInterface $request, ResponseInterface $response) {
109
+        $path = $request->getPath();
110
+        $node = $this->server->tree->getNodeForPath($path);
111
+        if (!$node instanceof EntityCollection) {
112
+            return null;
113
+        }
114
+
115
+        $data = $request->getBodyAsString();
116
+        $comment = $this->createComment(
117
+            $node->getName(),
118
+            $node->getId(),
119
+            $data,
120
+            $request->getHeader('Content-Type')
121
+        );
122
+
123
+        // update read marker for the current user/poster to avoid
124
+        // having their own comments marked as unread
125
+        $node->setReadMarker(null);
126
+
127
+        $url = rtrim($request->getUrl(), '/') . '/' . urlencode($comment->getId());
128
+
129
+        $response->setHeader('Content-Location', $url);
130
+
131
+        // created
132
+        $response->setStatus(201);
133
+        return false;
134
+    }
135
+
136
+    /**
137
+     * Returns a list of reports this plugin supports.
138
+     *
139
+     * This will be used in the {DAV:}supported-report-set property.
140
+     *
141
+     * @param string $uri
142
+     * @return array
143
+     */
144
+    public function getSupportedReportSet($uri) {
145
+        return [self::REPORT_NAME];
146
+    }
147
+
148
+    /**
149
+     * REPORT operations to look for comments
150
+     *
151
+     * @param string $reportName
152
+     * @param array $report
153
+     * @param string $uri
154
+     * @return bool
155
+     * @throws NotFound
156
+     * @throws ReportNotSupported
157
+     */
158
+    public function onReport($reportName, $report, $uri) {
159
+        $node = $this->server->tree->getNodeForPath($uri);
160
+        if(!$node instanceof EntityCollection || $reportName !== self::REPORT_NAME) {
161
+            throw new ReportNotSupported();
162
+        }
163
+        $args = ['limit' => 0, 'offset' => 0, 'datetime' => null];
164
+        $acceptableParameters = [
165
+            $this::REPORT_PARAM_LIMIT,
166
+            $this::REPORT_PARAM_OFFSET,
167
+            $this::REPORT_PARAM_TIMESTAMP
168
+        ];
169
+        $ns = '{' . $this::NS_OWNCLOUD . '}';
170
+        foreach($report as $parameter) {
171
+            if(!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) {
172
+                continue;
173
+            }
174
+            $args[str_replace($ns, '', $parameter['name'])] = $parameter['value'];
175
+        }
176
+
177
+        if(!is_null($args['datetime'])) {
178
+            $args['datetime'] = new \DateTime($args['datetime']);
179
+        }
180
+
181
+        $results = $node->findChildren($args['limit'], $args['offset'], $args['datetime']);
182
+
183
+        $responses = [];
184
+        foreach($results as $node) {
185
+            $nodePath = $this->server->getRequestUri() . '/' . $node->comment->getId();
186
+            $resultSet = $this->server->getPropertiesForPath($nodePath, CommentNode::getPropertyNames());
187
+            if(isset($resultSet[0]) && isset($resultSet[0][200])) {
188
+                $responses[] = new Response(
189
+                    $this->server->getBaseUri() . $nodePath,
190
+                    [200 => $resultSet[0][200]],
191
+                    200
192
+                );
193
+            }
194
+
195
+        }
196
+
197
+        $xml = $this->server->xml->write(
198
+            '{DAV:}multistatus',
199
+            new MultiStatus($responses)
200
+        );
201
+
202
+        $this->server->httpResponse->setStatus(207);
203
+        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
204
+        $this->server->httpResponse->setBody($xml);
205
+
206
+        return false;
207
+    }
208
+
209
+    /**
210
+     * Creates a new comment
211
+     *
212
+     * @param string $objectType e.g. "files"
213
+     * @param string $objectId e.g. the file id
214
+     * @param string $data JSON encoded string containing the properties of the tag to create
215
+     * @param string $contentType content type of the data
216
+     * @return IComment newly created comment
217
+     *
218
+     * @throws BadRequest if a field was missing
219
+     * @throws UnsupportedMediaType if the content type is not supported
220
+     */
221
+    private function createComment($objectType, $objectId, $data, $contentType = 'application/json') {
222
+        if (explode(';', $contentType)[0] === 'application/json') {
223
+            $data = json_decode($data, true);
224
+        } else {
225
+            throw new UnsupportedMediaType();
226
+        }
227
+
228
+        $actorType = $data['actorType'];
229
+        $actorId = null;
230
+        if($actorType === 'users') {
231
+            $user = $this->userSession->getUser();
232
+            if(!is_null($user)) {
233
+                $actorId = $user->getUID();
234
+            }
235
+        }
236
+        if(is_null($actorId)) {
237
+            throw new BadRequest('Invalid actor "' .  $actorType .'"');
238
+        }
239
+
240
+        try {
241
+            $comment = $this->commentsManager->create($actorType, $actorId, $objectType, $objectId);
242
+            $comment->setMessage($data['message']);
243
+            $comment->setVerb($data['verb']);
244
+            $this->commentsManager->save($comment);
245
+            return $comment;
246
+        } catch (\InvalidArgumentException $e) {
247
+            throw new BadRequest('Invalid input values', 0, $e);
248
+        } catch (\OCP\Comments\MessageTooLongException $e) {
249
+            $msg = 'Message exceeds allowed character limit of ';
250
+            throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0,	$e);
251
+        }
252
+    }
253 253
 
254 254
 
255 255
 
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -84,7 +84,7 @@  discard block
 block discarded – undo
84 84
 	 */
85 85
 	function initialize(Server $server) {
86 86
 		$this->server = $server;
87
-		if(strpos($this->server->getRequestUri(), 'comments/') !== 0) {
87
+		if (strpos($this->server->getRequestUri(), 'comments/') !== 0) {
88 88
 			return;
89 89
 		}
90 90
 
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
 		// having their own comments marked as unread
125 125
 		$node->setReadMarker(null);
126 126
 
127
-		$url = rtrim($request->getUrl(), '/') . '/' . urlencode($comment->getId());
127
+		$url = rtrim($request->getUrl(), '/').'/'.urlencode($comment->getId());
128 128
 
129 129
 		$response->setHeader('Content-Location', $url);
130 130
 
@@ -157,7 +157,7 @@  discard block
 block discarded – undo
157 157
 	 */
158 158
 	public function onReport($reportName, $report, $uri) {
159 159
 		$node = $this->server->tree->getNodeForPath($uri);
160
-		if(!$node instanceof EntityCollection || $reportName !== self::REPORT_NAME) {
160
+		if (!$node instanceof EntityCollection || $reportName !== self::REPORT_NAME) {
161 161
 			throw new ReportNotSupported();
162 162
 		}
163 163
 		$args = ['limit' => 0, 'offset' => 0, 'datetime' => null];
@@ -166,27 +166,27 @@  discard block
 block discarded – undo
166 166
 			$this::REPORT_PARAM_OFFSET,
167 167
 			$this::REPORT_PARAM_TIMESTAMP
168 168
 		];
169
-		$ns = '{' . $this::NS_OWNCLOUD . '}';
170
-		foreach($report as $parameter) {
171
-			if(!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) {
169
+		$ns = '{'.$this::NS_OWNCLOUD.'}';
170
+		foreach ($report as $parameter) {
171
+			if (!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) {
172 172
 				continue;
173 173
 			}
174 174
 			$args[str_replace($ns, '', $parameter['name'])] = $parameter['value'];
175 175
 		}
176 176
 
177
-		if(!is_null($args['datetime'])) {
177
+		if (!is_null($args['datetime'])) {
178 178
 			$args['datetime'] = new \DateTime($args['datetime']);
179 179
 		}
180 180
 
181 181
 		$results = $node->findChildren($args['limit'], $args['offset'], $args['datetime']);
182 182
 
183 183
 		$responses = [];
184
-		foreach($results as $node) {
185
-			$nodePath = $this->server->getRequestUri() . '/' . $node->comment->getId();
184
+		foreach ($results as $node) {
185
+			$nodePath = $this->server->getRequestUri().'/'.$node->comment->getId();
186 186
 			$resultSet = $this->server->getPropertiesForPath($nodePath, CommentNode::getPropertyNames());
187
-			if(isset($resultSet[0]) && isset($resultSet[0][200])) {
187
+			if (isset($resultSet[0]) && isset($resultSet[0][200])) {
188 188
 				$responses[] = new Response(
189
-					$this->server->getBaseUri() . $nodePath,
189
+					$this->server->getBaseUri().$nodePath,
190 190
 					[200 => $resultSet[0][200]],
191 191
 					200
192 192
 				);
@@ -227,14 +227,14 @@  discard block
 block discarded – undo
227 227
 
228 228
 		$actorType = $data['actorType'];
229 229
 		$actorId = null;
230
-		if($actorType === 'users') {
230
+		if ($actorType === 'users') {
231 231
 			$user = $this->userSession->getUser();
232
-			if(!is_null($user)) {
232
+			if (!is_null($user)) {
233 233
 				$actorId = $user->getUID();
234 234
 			}
235 235
 		}
236
-		if(is_null($actorId)) {
237
-			throw new BadRequest('Invalid actor "' .  $actorType .'"');
236
+		if (is_null($actorId)) {
237
+			throw new BadRequest('Invalid actor "'.$actorType.'"');
238 238
 		}
239 239
 
240 240
 		try {
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
 			throw new BadRequest('Invalid input values', 0, $e);
248 248
 		} catch (\OCP\Comments\MessageTooLongException $e) {
249 249
 			$msg = 'Message exceeds allowed character limit of ';
250
-			throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0,	$e);
250
+			throw new BadRequest($msg.\OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0, $e);
251 251
 		}
252 252
 	}
253 253
 
Please login to merge, or discard this patch.
apps/dav/lib/Comments/EntityCollection.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -115,7 +115,7 @@
 block discarded – undo
115 115
 	/**
116 116
 	 * Returns an array with all the child nodes
117 117
 	 *
118
-	 * @return \Sabre\DAV\INode[]
118
+	 * @return CommentNode[]
119 119
 	 */
120 120
 	function getChildren() {
121 121
 		return $this->findChildren();
Please login to merge, or discard this patch.
Indentation   +140 added lines, -140 removed lines patch added patch discarded remove patch
@@ -41,156 +41,156 @@
 block discarded – undo
41 41
  * @package OCA\DAV\Comments
42 42
  */
43 43
 class EntityCollection extends RootCollection implements IProperties {
44
-	const PROPERTY_NAME_READ_MARKER  = '{http://owncloud.org/ns}readMarker';
44
+    const PROPERTY_NAME_READ_MARKER  = '{http://owncloud.org/ns}readMarker';
45 45
 
46
-	/** @var  string */
47
-	protected $id;
46
+    /** @var  string */
47
+    protected $id;
48 48
 
49
-	/** @var  ILogger */
50
-	protected $logger;
49
+    /** @var  ILogger */
50
+    protected $logger;
51 51
 
52
-	/**
53
-	 * @param string $id
54
-	 * @param string $name
55
-	 * @param ICommentsManager $commentsManager
56
-	 * @param IUserManager $userManager
57
-	 * @param IUserSession $userSession
58
-	 * @param ILogger $logger
59
-	 */
60
-	public function __construct(
61
-		$id,
62
-		$name,
63
-		ICommentsManager $commentsManager,
64
-		IUserManager $userManager,
65
-		IUserSession $userSession,
66
-		ILogger $logger
67
-	) {
68
-		foreach(['id', 'name'] as $property) {
69
-			$$property = trim($$property);
70
-			if(empty($$property) || !is_string($$property)) {
71
-				throw new \InvalidArgumentException('"' . $property . '" parameter must be non-empty string');
72
-			}
73
-		}
74
-		$this->id = $id;
75
-		$this->name = $name;
76
-		$this->commentsManager = $commentsManager;
77
-		$this->logger = $logger;
78
-		$this->userManager = $userManager;
79
-		$this->userSession = $userSession;
80
-	}
52
+    /**
53
+     * @param string $id
54
+     * @param string $name
55
+     * @param ICommentsManager $commentsManager
56
+     * @param IUserManager $userManager
57
+     * @param IUserSession $userSession
58
+     * @param ILogger $logger
59
+     */
60
+    public function __construct(
61
+        $id,
62
+        $name,
63
+        ICommentsManager $commentsManager,
64
+        IUserManager $userManager,
65
+        IUserSession $userSession,
66
+        ILogger $logger
67
+    ) {
68
+        foreach(['id', 'name'] as $property) {
69
+            $$property = trim($$property);
70
+            if(empty($$property) || !is_string($$property)) {
71
+                throw new \InvalidArgumentException('"' . $property . '" parameter must be non-empty string');
72
+            }
73
+        }
74
+        $this->id = $id;
75
+        $this->name = $name;
76
+        $this->commentsManager = $commentsManager;
77
+        $this->logger = $logger;
78
+        $this->userManager = $userManager;
79
+        $this->userSession = $userSession;
80
+    }
81 81
 
82
-	/**
83
-	 * returns the ID of this entity
84
-	 *
85
-	 * @return string
86
-	 */
87
-	public function getId() {
88
-		return $this->id;
89
-	}
82
+    /**
83
+     * returns the ID of this entity
84
+     *
85
+     * @return string
86
+     */
87
+    public function getId() {
88
+        return $this->id;
89
+    }
90 90
 
91
-	/**
92
-	 * Returns a specific child node, referenced by its name
93
-	 *
94
-	 * This method must throw Sabre\DAV\Exception\NotFound if the node does not
95
-	 * exist.
96
-	 *
97
-	 * @param string $name
98
-	 * @return \Sabre\DAV\INode
99
-	 * @throws NotFound
100
-	 */
101
-	function getChild($name) {
102
-		try {
103
-			$comment = $this->commentsManager->get($name);
104
-			return new CommentNode(
105
-				$this->commentsManager,
106
-				$comment,
107
-				$this->userManager,
108
-				$this->userSession,
109
-				$this->logger
110
-			);
111
-		} catch (NotFoundException $e) {
112
-			throw new NotFound();
113
-		}
114
-	}
91
+    /**
92
+     * Returns a specific child node, referenced by its name
93
+     *
94
+     * This method must throw Sabre\DAV\Exception\NotFound if the node does not
95
+     * exist.
96
+     *
97
+     * @param string $name
98
+     * @return \Sabre\DAV\INode
99
+     * @throws NotFound
100
+     */
101
+    function getChild($name) {
102
+        try {
103
+            $comment = $this->commentsManager->get($name);
104
+            return new CommentNode(
105
+                $this->commentsManager,
106
+                $comment,
107
+                $this->userManager,
108
+                $this->userSession,
109
+                $this->logger
110
+            );
111
+        } catch (NotFoundException $e) {
112
+            throw new NotFound();
113
+        }
114
+    }
115 115
 
116
-	/**
117
-	 * Returns an array with all the child nodes
118
-	 *
119
-	 * @return \Sabre\DAV\INode[]
120
-	 */
121
-	function getChildren() {
122
-		return $this->findChildren();
123
-	}
116
+    /**
117
+     * Returns an array with all the child nodes
118
+     *
119
+     * @return \Sabre\DAV\INode[]
120
+     */
121
+    function getChildren() {
122
+        return $this->findChildren();
123
+    }
124 124
 
125
-	/**
126
-	 * Returns an array of comment nodes. Result can be influenced by offset,
127
-	 * limit and date time parameters.
128
-	 *
129
-	 * @param int $limit
130
-	 * @param int $offset
131
-	 * @param \DateTime|null $datetime
132
-	 * @return CommentNode[]
133
-	 */
134
-	function findChildren($limit = 0, $offset = 0, \DateTime $datetime = null) {
135
-		$comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime);
136
-		$result = [];
137
-		foreach($comments as $comment) {
138
-			$result[] = new CommentNode(
139
-				$this->commentsManager,
140
-				$comment,
141
-				$this->userManager,
142
-				$this->userSession,
143
-				$this->logger
144
-			);
145
-		}
146
-		return $result;
147
-	}
125
+    /**
126
+     * Returns an array of comment nodes. Result can be influenced by offset,
127
+     * limit and date time parameters.
128
+     *
129
+     * @param int $limit
130
+     * @param int $offset
131
+     * @param \DateTime|null $datetime
132
+     * @return CommentNode[]
133
+     */
134
+    function findChildren($limit = 0, $offset = 0, \DateTime $datetime = null) {
135
+        $comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime);
136
+        $result = [];
137
+        foreach($comments as $comment) {
138
+            $result[] = new CommentNode(
139
+                $this->commentsManager,
140
+                $comment,
141
+                $this->userManager,
142
+                $this->userSession,
143
+                $this->logger
144
+            );
145
+        }
146
+        return $result;
147
+    }
148 148
 
149
-	/**
150
-	 * Checks if a child-node with the specified name exists
151
-	 *
152
-	 * @param string $name
153
-	 * @return bool
154
-	 */
155
-	function childExists($name) {
156
-		try {
157
-			$this->commentsManager->get($name);
158
-			return true;
159
-		} catch (NotFoundException $e) {
160
-			return false;
161
-		}
162
-	}
149
+    /**
150
+     * Checks if a child-node with the specified name exists
151
+     *
152
+     * @param string $name
153
+     * @return bool
154
+     */
155
+    function childExists($name) {
156
+        try {
157
+            $this->commentsManager->get($name);
158
+            return true;
159
+        } catch (NotFoundException $e) {
160
+            return false;
161
+        }
162
+    }
163 163
 
164
-	/**
165
-	 * Sets the read marker to the specified date for the logged in user
166
-	 *
167
-	 * @param \DateTime $value
168
-	 * @return bool
169
-	 */
170
-	public function setReadMarker($value) {
171
-		$dateTime = new \DateTime($value);
172
-		$user = $this->userSession->getUser();
173
-		$this->commentsManager->setReadMark($this->name, $this->id, $dateTime, $user);
174
-		return true;
175
-	}
164
+    /**
165
+     * Sets the read marker to the specified date for the logged in user
166
+     *
167
+     * @param \DateTime $value
168
+     * @return bool
169
+     */
170
+    public function setReadMarker($value) {
171
+        $dateTime = new \DateTime($value);
172
+        $user = $this->userSession->getUser();
173
+        $this->commentsManager->setReadMark($this->name, $this->id, $dateTime, $user);
174
+        return true;
175
+    }
176 176
 
177
-	/**
178
-	 * @inheritdoc
179
-	 */
180
-	function propPatch(PropPatch $propPatch) {
181
-		$propPatch->handle(self::PROPERTY_NAME_READ_MARKER, [$this, 'setReadMarker']);
182
-	}
177
+    /**
178
+     * @inheritdoc
179
+     */
180
+    function propPatch(PropPatch $propPatch) {
181
+        $propPatch->handle(self::PROPERTY_NAME_READ_MARKER, [$this, 'setReadMarker']);
182
+    }
183 183
 
184
-	/**
185
-	 * @inheritdoc
186
-	 */
187
-	function getProperties($properties) {
188
-		$marker = null;
189
-		$user = $this->userSession->getUser();
190
-		if(!is_null($user)) {
191
-			$marker = $this->commentsManager->getReadMark($this->name, $this->id, $user);
192
-		}
193
-		return [self::PROPERTY_NAME_READ_MARKER => $marker];
194
-	}
184
+    /**
185
+     * @inheritdoc
186
+     */
187
+    function getProperties($properties) {
188
+        $marker = null;
189
+        $user = $this->userSession->getUser();
190
+        if(!is_null($user)) {
191
+            $marker = $this->commentsManager->getReadMark($this->name, $this->id, $user);
192
+        }
193
+        return [self::PROPERTY_NAME_READ_MARKER => $marker];
194
+    }
195 195
 }
196 196
 
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -41,7 +41,7 @@  discard block
 block discarded – undo
41 41
  * @package OCA\DAV\Comments
42 42
  */
43 43
 class EntityCollection extends RootCollection implements IProperties {
44
-	const PROPERTY_NAME_READ_MARKER  = '{http://owncloud.org/ns}readMarker';
44
+	const PROPERTY_NAME_READ_MARKER = '{http://owncloud.org/ns}readMarker';
45 45
 
46 46
 	/** @var  string */
47 47
 	protected $id;
@@ -65,10 +65,10 @@  discard block
 block discarded – undo
65 65
 		IUserSession $userSession,
66 66
 		ILogger $logger
67 67
 	) {
68
-		foreach(['id', 'name'] as $property) {
68
+		foreach (['id', 'name'] as $property) {
69 69
 			$$property = trim($$property);
70
-			if(empty($$property) || !is_string($$property)) {
71
-				throw new \InvalidArgumentException('"' . $property . '" parameter must be non-empty string');
70
+			if (empty($$property) || !is_string($$property)) {
71
+				throw new \InvalidArgumentException('"'.$property.'" parameter must be non-empty string');
72 72
 			}
73 73
 		}
74 74
 		$this->id = $id;
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
 	function findChildren($limit = 0, $offset = 0, \DateTime $datetime = null) {
135 135
 		$comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime);
136 136
 		$result = [];
137
-		foreach($comments as $comment) {
137
+		foreach ($comments as $comment) {
138 138
 			$result[] = new CommentNode(
139 139
 				$this->commentsManager,
140 140
 				$comment,
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
 	function getProperties($properties) {
188 188
 		$marker = null;
189 189
 		$user = $this->userSession->getUser();
190
-		if(!is_null($user)) {
190
+		if (!is_null($user)) {
191 191
 			$marker = $this->commentsManager->getReadMark($this->name, $this->id, $user);
192 192
 		}
193 193
 		return [self::PROPERTY_NAME_READ_MARKER => $marker];
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/CustomPropertiesBackend.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -75,7 +75,7 @@
 block discarded – undo
75 75
 	private $cache = [];
76 76
 
77 77
 	/**
78
-	 * @param Tree $tree node tree
78
+	 * @param ObjectTree $tree node tree
79 79
 	 * @param IDBConnection $connection database connection
80 80
 	 * @param IUser $user owner of the tree and properties
81 81
 	 */
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -111,7 +111,7 @@  discard block
 block discarded – undo
111 111
 			// we catch the exception to prevent breaking the whole list with a 404
112 112
 			// (soft fail)
113 113
 			\OC::$server->getLogger()->warning(
114
-				'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(),
114
+				'Could not get node for path: \"'.$path.'\" : '.$e->getMessage(),
115 115
 				array('app' => 'files')
116 116
 			);
117 117
 			return;
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
 		$statement = $this->connection->prepare(
171 171
 			'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
172 172
 		);
173
-		$statement->execute(array($this->user, '/' . $path));
173
+		$statement->execute(array($this->user, '/'.$path));
174 174
 		$statement->closeCursor();
175 175
 
176 176
 		unset($this->cache[$path]);
@@ -186,10 +186,10 @@  discard block
 block discarded – undo
186 186
 	 */
187 187
 	public function move($source, $destination) {
188 188
 		$statement = $this->connection->prepare(
189
-			'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
189
+			'UPDATE `*PREFIX*properties` SET `propertypath` = ?'.
190 190
 			' WHERE `userid` = ? AND `propertypath` = ?'
191 191
 		);
192
-		$statement->execute(array('/' . $destination, $this->user, '/' . $source));
192
+		$statement->execute(array('/'.$destination, $this->user, '/'.$source));
193 193
 		$statement->closeCursor();
194 194
 	}
195 195
 
@@ -250,13 +250,13 @@  discard block
 block discarded – undo
250 250
 	private function updateProperties($node, $properties) {
251 251
 		$path = $node->getPath();
252 252
 
253
-		$deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
253
+		$deleteStatement = 'DELETE FROM `*PREFIX*properties`'.
254 254
 			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
255 255
 
256
-		$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
256
+		$insertStatement = 'INSERT INTO `*PREFIX*properties`'.
257 257
 			' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
258 258
 
259
-		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
259
+		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?'.
260 260
 			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
261 261
 
262 262
 		// TODO: use "insert or update" strategy ?
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
 
330 330
 		$result = $this->connection->executeQuery(
331 331
 			$sql,
332
-			array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties),
332
+			array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')).'/%', $requestedProperties),
333 333
 			array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
334 334
 		);
335 335
 
Please login to merge, or discard this patch.
Indentation   +318 added lines, -318 removed lines patch added patch discarded remove patch
@@ -36,323 +36,323 @@
 block discarded – undo
36 36
 
37 37
 class CustomPropertiesBackend implements BackendInterface {
38 38
 
39
-	/**
40
-	 * Ignored properties
41
-	 *
42
-	 * @var array
43
-	 */
44
-	private $ignoredProperties = array(
45
-		'{DAV:}getcontentlength',
46
-		'{DAV:}getcontenttype',
47
-		'{DAV:}getetag',
48
-		'{DAV:}quota-used-bytes',
49
-		'{DAV:}quota-available-bytes',
50
-		'{DAV:}quota-available-bytes',
51
-		'{http://owncloud.org/ns}permissions',
52
-		'{http://owncloud.org/ns}downloadURL',
53
-		'{http://owncloud.org/ns}dDC',
54
-		'{http://owncloud.org/ns}size',
55
-		'{http://nextcloud.org/ns}is-encrypted',
56
-	);
57
-
58
-	/**
59
-	 * @var Tree
60
-	 */
61
-	private $tree;
62
-
63
-	/**
64
-	 * @var IDBConnection
65
-	 */
66
-	private $connection;
67
-
68
-	/**
69
-	 * @var IUser
70
-	 */
71
-	private $user;
72
-
73
-	/**
74
-	 * Properties cache
75
-	 *
76
-	 * @var array
77
-	 */
78
-	private $cache = [];
79
-
80
-	/**
81
-	 * @param Tree $tree node tree
82
-	 * @param IDBConnection $connection database connection
83
-	 * @param IUser $user owner of the tree and properties
84
-	 */
85
-	public function __construct(
86
-		Tree $tree,
87
-		IDBConnection $connection,
88
-		IUser $user) {
89
-		$this->tree = $tree;
90
-		$this->connection = $connection;
91
-		$this->user = $user->getUID();
92
-	}
93
-
94
-	/**
95
-	 * Fetches properties for a path.
96
-	 *
97
-	 * @param string $path
98
-	 * @param PropFind $propFind
99
-	 * @return void
100
-	 */
101
-	public function propFind($path, PropFind $propFind) {
102
-		try {
103
-			$node = $this->tree->getNodeForPath($path);
104
-			if (!($node instanceof Node)) {
105
-				return;
106
-			}
107
-		} catch (ServiceUnavailable $e) {
108
-			// might happen for unavailable mount points, skip
109
-			return;
110
-		} catch (NotFound $e) {
111
-			// in some rare (buggy) cases the node might not be found,
112
-			// we catch the exception to prevent breaking the whole list with a 404
113
-			// (soft fail)
114
-			\OC::$server->getLogger()->warning(
115
-				'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(),
116
-				array('app' => 'files')
117
-			);
118
-			return;
119
-		}
120
-
121
-		$requestedProps = $propFind->get404Properties();
122
-
123
-		// these might appear
124
-		$requestedProps = array_diff(
125
-			$requestedProps,
126
-			$this->ignoredProperties
127
-		);
128
-
129
-		if (empty($requestedProps)) {
130
-			return;
131
-		}
132
-
133
-		if ($node instanceof Directory
134
-			&& $propFind->getDepth() !== 0
135
-		) {
136
-			// note: pre-fetching only supported for depth <= 1
137
-			$this->loadChildrenProperties($node, $requestedProps);
138
-		}
139
-
140
-		$props = $this->getProperties($node, $requestedProps);
141
-		foreach ($props as $propName => $propValue) {
142
-			$propFind->set($propName, $propValue);
143
-		}
144
-	}
145
-
146
-	/**
147
-	 * Updates properties for a path
148
-	 *
149
-	 * @param string $path
150
-	 * @param PropPatch $propPatch
151
-	 *
152
-	 * @return void
153
-	 */
154
-	public function propPatch($path, PropPatch $propPatch) {
155
-		$node = $this->tree->getNodeForPath($path);
156
-		if (!($node instanceof Node)) {
157
-			return;
158
-		}
159
-
160
-		$propPatch->handleRemaining(function($changedProps) use ($node) {
161
-			return $this->updateProperties($node, $changedProps);
162
-		});
163
-	}
164
-
165
-	/**
166
-	 * This method is called after a node is deleted.
167
-	 *
168
-	 * @param string $path path of node for which to delete properties
169
-	 */
170
-	public function delete($path) {
171
-		$statement = $this->connection->prepare(
172
-			'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
173
-		);
174
-		$statement->execute(array($this->user, '/' . $path));
175
-		$statement->closeCursor();
176
-
177
-		unset($this->cache[$path]);
178
-	}
179
-
180
-	/**
181
-	 * This method is called after a successful MOVE
182
-	 *
183
-	 * @param string $source
184
-	 * @param string $destination
185
-	 *
186
-	 * @return void
187
-	 */
188
-	public function move($source, $destination) {
189
-		$statement = $this->connection->prepare(
190
-			'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
191
-			' WHERE `userid` = ? AND `propertypath` = ?'
192
-		);
193
-		$statement->execute(array('/' . $destination, $this->user, '/' . $source));
194
-		$statement->closeCursor();
195
-	}
196
-
197
-	/**
198
-	 * Returns a list of properties for this nodes.;
199
-	 * @param Node $node
200
-	 * @param array $requestedProperties requested properties or empty array for "all"
201
-	 * @return array
202
-	 * @note The properties list is a list of propertynames the client
203
-	 * requested, encoded as xmlnamespace#tagName, for example:
204
-	 * http://www.example.org/namespace#author If the array is empty, all
205
-	 * properties should be returned
206
-	 */
207
-	private function getProperties(Node $node, array $requestedProperties) {
208
-		$path = $node->getPath();
209
-		if (isset($this->cache[$path])) {
210
-			return $this->cache[$path];
211
-		}
212
-
213
-		// TODO: chunking if more than 1000 properties
214
-		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
215
-
216
-		$whereValues = array($this->user, $path);
217
-		$whereTypes = array(null, null);
218
-
219
-		if (!empty($requestedProperties)) {
220
-			// request only a subset
221
-			$sql .= ' AND `propertyname` in (?)';
222
-			$whereValues[] = $requestedProperties;
223
-			$whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
224
-		}
225
-
226
-		$result = $this->connection->executeQuery(
227
-			$sql,
228
-			$whereValues,
229
-			$whereTypes
230
-		);
231
-
232
-		$props = [];
233
-		while ($row = $result->fetch()) {
234
-			$props[$row['propertyname']] = $row['propertyvalue'];
235
-		}
236
-
237
-		$result->closeCursor();
238
-
239
-		$this->cache[$path] = $props;
240
-		return $props;
241
-	}
242
-
243
-	/**
244
-	 * Update properties
245
-	 *
246
-	 * @param Node $node node for which to update properties
247
-	 * @param array $properties array of properties to update
248
-	 *
249
-	 * @return bool
250
-	 */
251
-	private function updateProperties($node, $properties) {
252
-		$path = $node->getPath();
253
-
254
-		$deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
255
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
256
-
257
-		$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
258
-			' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
259
-
260
-		$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
261
-			' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
262
-
263
-		// TODO: use "insert or update" strategy ?
264
-		$existing = $this->getProperties($node, array());
265
-		$this->connection->beginTransaction();
266
-		foreach ($properties as $propertyName => $propertyValue) {
267
-			// If it was null, we need to delete the property
268
-			if (is_null($propertyValue)) {
269
-				if (array_key_exists($propertyName, $existing)) {
270
-					$this->connection->executeUpdate($deleteStatement,
271
-						array(
272
-							$this->user,
273
-							$path,
274
-							$propertyName
275
-						)
276
-					);
277
-				}
278
-			} else {
279
-				if (!array_key_exists($propertyName, $existing)) {
280
-					$this->connection->executeUpdate($insertStatement,
281
-						array(
282
-							$this->user,
283
-							$path,
284
-							$propertyName,
285
-							$propertyValue
286
-						)
287
-					);
288
-				} else {
289
-					$this->connection->executeUpdate($updateStatement,
290
-						array(
291
-							$propertyValue,
292
-							$this->user,
293
-							$path,
294
-							$propertyName
295
-						)
296
-					);
297
-				}
298
-			}
299
-		}
300
-
301
-		$this->connection->commit();
302
-		unset($this->cache[$path]);
303
-
304
-		return true;
305
-	}
306
-
307
-	/**
308
-	 * Bulk load properties for directory children
309
-	 *
310
-	 * @param Directory $node
311
-	 * @param array $requestedProperties requested properties
312
-	 *
313
-	 * @return void
314
-	 */
315
-	private function loadChildrenProperties(Directory $node, $requestedProperties) {
316
-		$path = $node->getPath();
317
-		if (isset($this->cache[$path])) {
318
-			// we already loaded them at some point
319
-			return;
320
-		}
321
-
322
-		$childNodes = $node->getChildren();
323
-		// pre-fill cache
324
-		foreach ($childNodes as $childNode) {
325
-			$this->cache[$childNode->getPath()] = [];
326
-		}
327
-
328
-		$sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?';
329
-		$sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`';
330
-
331
-		$result = $this->connection->executeQuery(
332
-			$sql,
333
-			array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties),
334
-			array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
335
-		);
336
-
337
-		$oldPath = null;
338
-		$props = [];
339
-		while ($row = $result->fetch()) {
340
-			$path = $row['propertypath'];
341
-			if ($oldPath !== $path) {
342
-				// save previously gathered props
343
-				$this->cache[$oldPath] = $props;
344
-				$oldPath = $path;
345
-				// prepare props for next path
346
-				$props = [];
347
-			}
348
-			$props[$row['propertyname']] = $row['propertyvalue'];
349
-		}
350
-		if (!is_null($oldPath)) {
351
-			// save props from last run
352
-			$this->cache[$oldPath] = $props;
353
-		}
354
-
355
-		$result->closeCursor();
356
-	}
39
+    /**
40
+     * Ignored properties
41
+     *
42
+     * @var array
43
+     */
44
+    private $ignoredProperties = array(
45
+        '{DAV:}getcontentlength',
46
+        '{DAV:}getcontenttype',
47
+        '{DAV:}getetag',
48
+        '{DAV:}quota-used-bytes',
49
+        '{DAV:}quota-available-bytes',
50
+        '{DAV:}quota-available-bytes',
51
+        '{http://owncloud.org/ns}permissions',
52
+        '{http://owncloud.org/ns}downloadURL',
53
+        '{http://owncloud.org/ns}dDC',
54
+        '{http://owncloud.org/ns}size',
55
+        '{http://nextcloud.org/ns}is-encrypted',
56
+    );
57
+
58
+    /**
59
+     * @var Tree
60
+     */
61
+    private $tree;
62
+
63
+    /**
64
+     * @var IDBConnection
65
+     */
66
+    private $connection;
67
+
68
+    /**
69
+     * @var IUser
70
+     */
71
+    private $user;
72
+
73
+    /**
74
+     * Properties cache
75
+     *
76
+     * @var array
77
+     */
78
+    private $cache = [];
79
+
80
+    /**
81
+     * @param Tree $tree node tree
82
+     * @param IDBConnection $connection database connection
83
+     * @param IUser $user owner of the tree and properties
84
+     */
85
+    public function __construct(
86
+        Tree $tree,
87
+        IDBConnection $connection,
88
+        IUser $user) {
89
+        $this->tree = $tree;
90
+        $this->connection = $connection;
91
+        $this->user = $user->getUID();
92
+    }
93
+
94
+    /**
95
+     * Fetches properties for a path.
96
+     *
97
+     * @param string $path
98
+     * @param PropFind $propFind
99
+     * @return void
100
+     */
101
+    public function propFind($path, PropFind $propFind) {
102
+        try {
103
+            $node = $this->tree->getNodeForPath($path);
104
+            if (!($node instanceof Node)) {
105
+                return;
106
+            }
107
+        } catch (ServiceUnavailable $e) {
108
+            // might happen for unavailable mount points, skip
109
+            return;
110
+        } catch (NotFound $e) {
111
+            // in some rare (buggy) cases the node might not be found,
112
+            // we catch the exception to prevent breaking the whole list with a 404
113
+            // (soft fail)
114
+            \OC::$server->getLogger()->warning(
115
+                'Could not get node for path: \"' . $path . '\" : ' . $e->getMessage(),
116
+                array('app' => 'files')
117
+            );
118
+            return;
119
+        }
120
+
121
+        $requestedProps = $propFind->get404Properties();
122
+
123
+        // these might appear
124
+        $requestedProps = array_diff(
125
+            $requestedProps,
126
+            $this->ignoredProperties
127
+        );
128
+
129
+        if (empty($requestedProps)) {
130
+            return;
131
+        }
132
+
133
+        if ($node instanceof Directory
134
+            && $propFind->getDepth() !== 0
135
+        ) {
136
+            // note: pre-fetching only supported for depth <= 1
137
+            $this->loadChildrenProperties($node, $requestedProps);
138
+        }
139
+
140
+        $props = $this->getProperties($node, $requestedProps);
141
+        foreach ($props as $propName => $propValue) {
142
+            $propFind->set($propName, $propValue);
143
+        }
144
+    }
145
+
146
+    /**
147
+     * Updates properties for a path
148
+     *
149
+     * @param string $path
150
+     * @param PropPatch $propPatch
151
+     *
152
+     * @return void
153
+     */
154
+    public function propPatch($path, PropPatch $propPatch) {
155
+        $node = $this->tree->getNodeForPath($path);
156
+        if (!($node instanceof Node)) {
157
+            return;
158
+        }
159
+
160
+        $propPatch->handleRemaining(function($changedProps) use ($node) {
161
+            return $this->updateProperties($node, $changedProps);
162
+        });
163
+    }
164
+
165
+    /**
166
+     * This method is called after a node is deleted.
167
+     *
168
+     * @param string $path path of node for which to delete properties
169
+     */
170
+    public function delete($path) {
171
+        $statement = $this->connection->prepare(
172
+            'DELETE FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'
173
+        );
174
+        $statement->execute(array($this->user, '/' . $path));
175
+        $statement->closeCursor();
176
+
177
+        unset($this->cache[$path]);
178
+    }
179
+
180
+    /**
181
+     * This method is called after a successful MOVE
182
+     *
183
+     * @param string $source
184
+     * @param string $destination
185
+     *
186
+     * @return void
187
+     */
188
+    public function move($source, $destination) {
189
+        $statement = $this->connection->prepare(
190
+            'UPDATE `*PREFIX*properties` SET `propertypath` = ?' .
191
+            ' WHERE `userid` = ? AND `propertypath` = ?'
192
+        );
193
+        $statement->execute(array('/' . $destination, $this->user, '/' . $source));
194
+        $statement->closeCursor();
195
+    }
196
+
197
+    /**
198
+     * Returns a list of properties for this nodes.;
199
+     * @param Node $node
200
+     * @param array $requestedProperties requested properties or empty array for "all"
201
+     * @return array
202
+     * @note The properties list is a list of propertynames the client
203
+     * requested, encoded as xmlnamespace#tagName, for example:
204
+     * http://www.example.org/namespace#author If the array is empty, all
205
+     * properties should be returned
206
+     */
207
+    private function getProperties(Node $node, array $requestedProperties) {
208
+        $path = $node->getPath();
209
+        if (isset($this->cache[$path])) {
210
+            return $this->cache[$path];
211
+        }
212
+
213
+        // TODO: chunking if more than 1000 properties
214
+        $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?';
215
+
216
+        $whereValues = array($this->user, $path);
217
+        $whereTypes = array(null, null);
218
+
219
+        if (!empty($requestedProperties)) {
220
+            // request only a subset
221
+            $sql .= ' AND `propertyname` in (?)';
222
+            $whereValues[] = $requestedProperties;
223
+            $whereTypes[] = \Doctrine\DBAL\Connection::PARAM_STR_ARRAY;
224
+        }
225
+
226
+        $result = $this->connection->executeQuery(
227
+            $sql,
228
+            $whereValues,
229
+            $whereTypes
230
+        );
231
+
232
+        $props = [];
233
+        while ($row = $result->fetch()) {
234
+            $props[$row['propertyname']] = $row['propertyvalue'];
235
+        }
236
+
237
+        $result->closeCursor();
238
+
239
+        $this->cache[$path] = $props;
240
+        return $props;
241
+    }
242
+
243
+    /**
244
+     * Update properties
245
+     *
246
+     * @param Node $node node for which to update properties
247
+     * @param array $properties array of properties to update
248
+     *
249
+     * @return bool
250
+     */
251
+    private function updateProperties($node, $properties) {
252
+        $path = $node->getPath();
253
+
254
+        $deleteStatement = 'DELETE FROM `*PREFIX*properties`' .
255
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
256
+
257
+        $insertStatement = 'INSERT INTO `*PREFIX*properties`' .
258
+            ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
259
+
260
+        $updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
261
+            ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
262
+
263
+        // TODO: use "insert or update" strategy ?
264
+        $existing = $this->getProperties($node, array());
265
+        $this->connection->beginTransaction();
266
+        foreach ($properties as $propertyName => $propertyValue) {
267
+            // If it was null, we need to delete the property
268
+            if (is_null($propertyValue)) {
269
+                if (array_key_exists($propertyName, $existing)) {
270
+                    $this->connection->executeUpdate($deleteStatement,
271
+                        array(
272
+                            $this->user,
273
+                            $path,
274
+                            $propertyName
275
+                        )
276
+                    );
277
+                }
278
+            } else {
279
+                if (!array_key_exists($propertyName, $existing)) {
280
+                    $this->connection->executeUpdate($insertStatement,
281
+                        array(
282
+                            $this->user,
283
+                            $path,
284
+                            $propertyName,
285
+                            $propertyValue
286
+                        )
287
+                    );
288
+                } else {
289
+                    $this->connection->executeUpdate($updateStatement,
290
+                        array(
291
+                            $propertyValue,
292
+                            $this->user,
293
+                            $path,
294
+                            $propertyName
295
+                        )
296
+                    );
297
+                }
298
+            }
299
+        }
300
+
301
+        $this->connection->commit();
302
+        unset($this->cache[$path]);
303
+
304
+        return true;
305
+    }
306
+
307
+    /**
308
+     * Bulk load properties for directory children
309
+     *
310
+     * @param Directory $node
311
+     * @param array $requestedProperties requested properties
312
+     *
313
+     * @return void
314
+     */
315
+    private function loadChildrenProperties(Directory $node, $requestedProperties) {
316
+        $path = $node->getPath();
317
+        if (isset($this->cache[$path])) {
318
+            // we already loaded them at some point
319
+            return;
320
+        }
321
+
322
+        $childNodes = $node->getChildren();
323
+        // pre-fill cache
324
+        foreach ($childNodes as $childNode) {
325
+            $this->cache[$childNode->getPath()] = [];
326
+        }
327
+
328
+        $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` LIKE ?';
329
+        $sql .= ' AND `propertyname` in (?) ORDER BY `propertypath`, `propertyname`';
330
+
331
+        $result = $this->connection->executeQuery(
332
+            $sql,
333
+            array($this->user, $this->connection->escapeLikeParameter(rtrim($path, '/')) . '/%', $requestedProperties),
334
+            array(null, null, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
335
+        );
336
+
337
+        $oldPath = null;
338
+        $props = [];
339
+        while ($row = $result->fetch()) {
340
+            $path = $row['propertypath'];
341
+            if ($oldPath !== $path) {
342
+                // save previously gathered props
343
+                $this->cache[$oldPath] = $props;
344
+                $oldPath = $path;
345
+                // prepare props for next path
346
+                $props = [];
347
+            }
348
+            $props[$row['propertyname']] = $row['propertyvalue'];
349
+        }
350
+        if (!is_null($oldPath)) {
351
+            // save props from last run
352
+            $this->cache[$oldPath] = $props;
353
+        }
354
+
355
+        $result->closeCursor();
356
+    }
357 357
 
358 358
 }
Please login to merge, or discard this patch.
apps/dav/lib/DAV/SystemPrincipalBackend.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -132,7 +132,7 @@
 block discarded – undo
132 132
 	 * Returns the list of members for a group-principal
133 133
 	 *
134 134
 	 * @param string $principal
135
-	 * @return array
135
+	 * @return string[]
136 136
 	 */
137 137
 	function getGroupMemberSet($principal) {
138 138
 		// TODO: for now the group principal has only one member, the user itself
Please login to merge, or discard this patch.
Indentation   +161 added lines, -161 removed lines patch added patch discarded remove patch
@@ -27,165 +27,165 @@
 block discarded – undo
27 27
 
28 28
 class SystemPrincipalBackend extends AbstractBackend {
29 29
 
30
-	/**
31
-	 * Returns a list of principals based on a prefix.
32
-	 *
33
-	 * This prefix will often contain something like 'principals'. You are only
34
-	 * expected to return principals that are in this base path.
35
-	 *
36
-	 * You are expected to return at least a 'uri' for every user, you can
37
-	 * return any additional properties if you wish so. Common properties are:
38
-	 *   {DAV:}displayname
39
-	 *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV
40
-	 *     field that's actually injected in a number of other properties. If
41
-	 *     you have an email address, use this property.
42
-	 *
43
-	 * @param string $prefixPath
44
-	 * @return array
45
-	 */
46
-	function getPrincipalsByPrefix($prefixPath) {
47
-		$principals = [];
48
-
49
-		if ($prefixPath === 'principals/system') {
50
-			$principals[] = [
51
-				'uri' => 'principals/system/system',
52
-				'{DAV:}displayname' => 'system',
53
-			];
54
-			$principals[] = [
55
-				'uri' => 'principals/system/public',
56
-				'{DAV:}displayname' => 'public',
57
-			];
58
-		}
59
-
60
-		return $principals;
61
-	}
62
-
63
-	/**
64
-	 * Returns a specific principal, specified by it's path.
65
-	 * The returned structure should be the exact same as from
66
-	 * getPrincipalsByPrefix.
67
-	 *
68
-	 * @param string $path
69
-	 * @return array
70
-	 */
71
-	function getPrincipalByPath($path) {
72
-
73
-		if ($path === 'principals/system/system') {
74
-			$principal = [
75
-				'uri' => 'principals/system/system',
76
-				'{DAV:}displayname' => 'system',
77
-			];
78
-			return $principal;
79
-		}
80
-		if ($path === 'principals/system/public') {
81
-			$principal = [
82
-				'uri' => 'principals/system/public',
83
-				'{DAV:}displayname' => 'public',
84
-			];
85
-			return $principal;
86
-		}
87
-
88
-		return null;
89
-	}
90
-
91
-	/**
92
-	 * Updates one ore more webdav properties on a principal.
93
-	 *
94
-	 * The list of mutations is stored in a Sabre\DAV\PropPatch object.
95
-	 * To do the actual updates, you must tell this object which properties
96
-	 * you're going to process with the handle() method.
97
-	 *
98
-	 * Calling the handle method is like telling the PropPatch object "I
99
-	 * promise I can handle updating this property".
100
-	 *
101
-	 * Read the PropPatch documentation for more info and examples.
102
-	 *
103
-	 * @param string $path
104
-	 * @param \Sabre\DAV\PropPatch $propPatch
105
-	 * @return void
106
-	 */
107
-	function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
108
-	}
109
-
110
-	/**
111
-	 * This method is used to search for principals matching a set of
112
-	 * properties.
113
-	 *
114
-	 * This search is specifically used by RFC3744's principal-property-search
115
-	 * REPORT.
116
-	 *
117
-	 * The actual search should be a unicode-non-case-sensitive search. The
118
-	 * keys in searchProperties are the WebDAV property names, while the values
119
-	 * are the property values to search on.
120
-	 *
121
-	 * By default, if multiple properties are submitted to this method, the
122
-	 * various properties should be combined with 'AND'. If $test is set to
123
-	 * 'anyof', it should be combined using 'OR'.
124
-	 *
125
-	 * This method should simply return an array with full principal uri's.
126
-	 *
127
-	 * If somebody attempted to search on a property the backend does not
128
-	 * support, you should simply return 0 results.
129
-	 *
130
-	 * You can also just return 0 results if you choose to not support
131
-	 * searching at all, but keep in mind that this may stop certain features
132
-	 * from working.
133
-	 *
134
-	 * @param string $prefixPath
135
-	 * @param array $searchProperties
136
-	 * @param string $test
137
-	 * @return array
138
-	 */
139
-	function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
140
-		return [];
141
-	}
142
-
143
-	/**
144
-	 * Returns the list of members for a group-principal
145
-	 *
146
-	 * @param string $principal
147
-	 * @return array
148
-	 */
149
-	function getGroupMemberSet($principal) {
150
-		// TODO: for now the group principal has only one member, the user itself
151
-		$principal = $this->getPrincipalByPath($principal);
152
-		if (!$principal) {
153
-			throw new \Sabre\DAV\Exception('Principal not found');
154
-		}
155
-
156
-		return [$principal['uri']];
157
-	}
158
-
159
-	/**
160
-	 * Returns the list of groups a principal is a member of
161
-	 *
162
-	 * @param string $principal
163
-	 * @return array
164
-	 */
165
-	function getGroupMembership($principal) {
166
-		list($prefix, ) = \Sabre\Uri\split($principal);
167
-
168
-		if ($prefix === 'principals/system') {
169
-			$principal = $this->getPrincipalByPath($principal);
170
-			if (!$principal) {
171
-				throw new \Sabre\DAV\Exception('Principal not found');
172
-			}
173
-
174
-			return [];
175
-		}
176
-		return [];
177
-	}
178
-
179
-	/**
180
-	 * Updates the list of group members for a group principal.
181
-	 *
182
-	 * The principals should be passed as a list of uri's.
183
-	 *
184
-	 * @param string $principal
185
-	 * @param array $members
186
-	 * @return void
187
-	 */
188
-	function setGroupMemberSet($principal, array $members) {
189
-		throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
190
-	}
30
+    /**
31
+     * Returns a list of principals based on a prefix.
32
+     *
33
+     * This prefix will often contain something like 'principals'. You are only
34
+     * expected to return principals that are in this base path.
35
+     *
36
+     * You are expected to return at least a 'uri' for every user, you can
37
+     * return any additional properties if you wish so. Common properties are:
38
+     *   {DAV:}displayname
39
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV
40
+     *     field that's actually injected in a number of other properties. If
41
+     *     you have an email address, use this property.
42
+     *
43
+     * @param string $prefixPath
44
+     * @return array
45
+     */
46
+    function getPrincipalsByPrefix($prefixPath) {
47
+        $principals = [];
48
+
49
+        if ($prefixPath === 'principals/system') {
50
+            $principals[] = [
51
+                'uri' => 'principals/system/system',
52
+                '{DAV:}displayname' => 'system',
53
+            ];
54
+            $principals[] = [
55
+                'uri' => 'principals/system/public',
56
+                '{DAV:}displayname' => 'public',
57
+            ];
58
+        }
59
+
60
+        return $principals;
61
+    }
62
+
63
+    /**
64
+     * Returns a specific principal, specified by it's path.
65
+     * The returned structure should be the exact same as from
66
+     * getPrincipalsByPrefix.
67
+     *
68
+     * @param string $path
69
+     * @return array
70
+     */
71
+    function getPrincipalByPath($path) {
72
+
73
+        if ($path === 'principals/system/system') {
74
+            $principal = [
75
+                'uri' => 'principals/system/system',
76
+                '{DAV:}displayname' => 'system',
77
+            ];
78
+            return $principal;
79
+        }
80
+        if ($path === 'principals/system/public') {
81
+            $principal = [
82
+                'uri' => 'principals/system/public',
83
+                '{DAV:}displayname' => 'public',
84
+            ];
85
+            return $principal;
86
+        }
87
+
88
+        return null;
89
+    }
90
+
91
+    /**
92
+     * Updates one ore more webdav properties on a principal.
93
+     *
94
+     * The list of mutations is stored in a Sabre\DAV\PropPatch object.
95
+     * To do the actual updates, you must tell this object which properties
96
+     * you're going to process with the handle() method.
97
+     *
98
+     * Calling the handle method is like telling the PropPatch object "I
99
+     * promise I can handle updating this property".
100
+     *
101
+     * Read the PropPatch documentation for more info and examples.
102
+     *
103
+     * @param string $path
104
+     * @param \Sabre\DAV\PropPatch $propPatch
105
+     * @return void
106
+     */
107
+    function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
108
+    }
109
+
110
+    /**
111
+     * This method is used to search for principals matching a set of
112
+     * properties.
113
+     *
114
+     * This search is specifically used by RFC3744's principal-property-search
115
+     * REPORT.
116
+     *
117
+     * The actual search should be a unicode-non-case-sensitive search. The
118
+     * keys in searchProperties are the WebDAV property names, while the values
119
+     * are the property values to search on.
120
+     *
121
+     * By default, if multiple properties are submitted to this method, the
122
+     * various properties should be combined with 'AND'. If $test is set to
123
+     * 'anyof', it should be combined using 'OR'.
124
+     *
125
+     * This method should simply return an array with full principal uri's.
126
+     *
127
+     * If somebody attempted to search on a property the backend does not
128
+     * support, you should simply return 0 results.
129
+     *
130
+     * You can also just return 0 results if you choose to not support
131
+     * searching at all, but keep in mind that this may stop certain features
132
+     * from working.
133
+     *
134
+     * @param string $prefixPath
135
+     * @param array $searchProperties
136
+     * @param string $test
137
+     * @return array
138
+     */
139
+    function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
140
+        return [];
141
+    }
142
+
143
+    /**
144
+     * Returns the list of members for a group-principal
145
+     *
146
+     * @param string $principal
147
+     * @return array
148
+     */
149
+    function getGroupMemberSet($principal) {
150
+        // TODO: for now the group principal has only one member, the user itself
151
+        $principal = $this->getPrincipalByPath($principal);
152
+        if (!$principal) {
153
+            throw new \Sabre\DAV\Exception('Principal not found');
154
+        }
155
+
156
+        return [$principal['uri']];
157
+    }
158
+
159
+    /**
160
+     * Returns the list of groups a principal is a member of
161
+     *
162
+     * @param string $principal
163
+     * @return array
164
+     */
165
+    function getGroupMembership($principal) {
166
+        list($prefix, ) = \Sabre\Uri\split($principal);
167
+
168
+        if ($prefix === 'principals/system') {
169
+            $principal = $this->getPrincipalByPath($principal);
170
+            if (!$principal) {
171
+                throw new \Sabre\DAV\Exception('Principal not found');
172
+            }
173
+
174
+            return [];
175
+        }
176
+        return [];
177
+    }
178
+
179
+    /**
180
+     * Updates the list of group members for a group principal.
181
+     *
182
+     * The principals should be passed as a list of uri's.
183
+     *
184
+     * @param string $principal
185
+     * @param array $members
186
+     * @return void
187
+     */
188
+    function setGroupMemberSet($principal, array $members) {
189
+        throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
190
+    }
191 191
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -163,7 +163,7 @@
 block discarded – undo
163 163
 	 * @return array
164 164
 	 */
165 165
 	function getGroupMembership($principal) {
166
-		list($prefix, ) = \Sabre\Uri\split($principal);
166
+		list($prefix,) = \Sabre\Uri\split($principal);
167 167
 
168 168
 		if ($prefix === 'principals/system') {
169 169
 			$principal = $this->getPrincipalByPath($principal);
Please login to merge, or discard this patch.
apps/encryption/lib/Crypto/Encryption.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -369,7 +369,7 @@
 block discarded – undo
369 369
 	 * @param string $path path to the file which should be updated
370 370
 	 * @param string $uid of the user who performs the operation
371 371
 	 * @param array $accessList who has access to the file contains the key 'users' and 'public'
372
-	 * @return boolean
372
+	 * @return null|boolean
373 373
 	 */
374 374
 	public function update($path, $uid, array $accessList) {
375 375
 
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -177,7 +177,7 @@  discard block
 block discarded – undo
177 177
 		$this->isWriteOperation = false;
178 178
 		$this->writeCache = '';
179 179
 
180
-		if($this->session->isReady() === false) {
180
+		if ($this->session->isReady() === false) {
181 181
 			// if the master key is enabled we can initialize encryption
182 182
 			// with a empty password and user name
183 183
 			if ($this->util->isMasterKeyEnabled()) {
@@ -198,7 +198,7 @@  discard block
 block discarded – undo
198 198
 		// always use the version from the original file, also part files
199 199
 		// need to have a correct version number if they get moved over to the
200 200
 		// final location
201
-		$this->version = (int)$this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
201
+		$this->version = (int) $this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
202 202
 
203 203
 		if (
204 204
 			$mode === 'w'
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 			// if we read a part file we need to increase the version by 1
215 215
 			// because the version number was also increased by writing
216 216
 			// the part file
217
-			if(Scanner::isPartialFile($path)) {
217
+			if (Scanner::isPartialFile($path)) {
218 218
 				$this->version = $this->version + 1;
219 219
 			}
220 220
 		}
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
 		if ($this->writeCache) {
301 301
 
302 302
 			// Concat writeCache to start of $data
303
-			$data = $this->writeCache . $data;
303
+			$data = $this->writeCache.$data;
304 304
 
305 305
 			// Clear the write cache, ready for reuse - it has been
306 306
 			// flushed and its old contents processed
@@ -402,7 +402,7 @@  discard block
 block discarded – undo
402 402
 					try {
403 403
 						$publicKeys[$user] = $this->keyManager->getPublicKey($user);
404 404
 					} catch (PublicKeyMissingException $e) {
405
-						$this->logger->warning('Could not encrypt file for ' . $user . ': ' . $e->getMessage());
405
+						$this->logger->warning('Could not encrypt file for '.$user.': '.$e->getMessage());
406 406
 					}
407 407
 				}
408 408
 			}
@@ -489,8 +489,8 @@  discard block
 block discarded – undo
489 489
 				// error message because in this case it means that the file was
490 490
 				// shared with the user at a point where the user didn't had a
491 491
 				// valid private/public key
492
-				$msg = 'Encryption module "' . $this->getDisplayName() .
493
-					'" is not able to read ' . $path;
492
+				$msg = 'Encryption module "'.$this->getDisplayName().
493
+					'" is not able to read '.$path;
494 494
 				$hint = $this->l->t('Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
495 495
 				$this->logger->warning($msg);
496 496
 				throw new DecryptionFailedException($msg, $hint);
@@ -532,7 +532,7 @@  discard block
 block discarded – undo
532 532
 		$realPath = $path;
533 533
 		$parts = explode('/', $path);
534 534
 		if ($parts[2] === 'files_versions') {
535
-			$realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
535
+			$realPath = '/'.$parts[1].'/files/'.implode('/', array_slice($parts, 3));
536 536
 			$length = strrpos($realPath, '.');
537 537
 			$realPath = substr($realPath, 0, $length);
538 538
 		}
Please login to merge, or discard this patch.
Indentation   +554 added lines, -554 removed lines patch added patch discarded remove patch
@@ -43,558 +43,558 @@
 block discarded – undo
43 43
 
44 44
 class Encryption implements IEncryptionModule {
45 45
 
46
-	const ID = 'OC_DEFAULT_MODULE';
47
-	const DISPLAY_NAME = 'Default encryption module';
48
-
49
-	/**
50
-	 * @var Crypt
51
-	 */
52
-	private $crypt;
53
-
54
-	/** @var string */
55
-	private $cipher;
56
-
57
-	/** @var string */
58
-	private $path;
59
-
60
-	/** @var string */
61
-	private $user;
62
-
63
-	/** @var  array */
64
-	private $owner;
65
-
66
-	/** @var string */
67
-	private $fileKey;
68
-
69
-	/** @var string */
70
-	private $writeCache;
71
-
72
-	/** @var KeyManager */
73
-	private $keyManager;
74
-
75
-	/** @var array */
76
-	private $accessList;
77
-
78
-	/** @var boolean */
79
-	private $isWriteOperation;
80
-
81
-	/** @var Util */
82
-	private $util;
83
-
84
-	/** @var  Session */
85
-	private $session;
86
-
87
-	/** @var  ILogger */
88
-	private $logger;
89
-
90
-	/** @var IL10N */
91
-	private $l;
92
-
93
-	/** @var EncryptAll */
94
-	private $encryptAll;
95
-
96
-	/** @var  bool */
97
-	private $useMasterPassword;
98
-
99
-	/** @var DecryptAll  */
100
-	private $decryptAll;
101
-
102
-	/** @var int unencrypted block size if block contains signature */
103
-	private $unencryptedBlockSizeSigned = 6072;
104
-
105
-	/** @var int unencrypted block size */
106
-	private $unencryptedBlockSize = 6126;
107
-
108
-	/** @var int Current version of the file */
109
-	private $version = 0;
110
-
111
-	/** @var array remember encryption signature version */
112
-	private static $rememberVersion = [];
113
-
114
-
115
-	/**
116
-	 *
117
-	 * @param Crypt $crypt
118
-	 * @param KeyManager $keyManager
119
-	 * @param Util $util
120
-	 * @param Session $session
121
-	 * @param EncryptAll $encryptAll
122
-	 * @param DecryptAll $decryptAll
123
-	 * @param ILogger $logger
124
-	 * @param IL10N $il10n
125
-	 */
126
-	public function __construct(Crypt $crypt,
127
-								KeyManager $keyManager,
128
-								Util $util,
129
-								Session $session,
130
-								EncryptAll $encryptAll,
131
-								DecryptAll $decryptAll,
132
-								ILogger $logger,
133
-								IL10N $il10n) {
134
-		$this->crypt = $crypt;
135
-		$this->keyManager = $keyManager;
136
-		$this->util = $util;
137
-		$this->session = $session;
138
-		$this->encryptAll = $encryptAll;
139
-		$this->decryptAll = $decryptAll;
140
-		$this->logger = $logger;
141
-		$this->l = $il10n;
142
-		$this->owner = [];
143
-		$this->useMasterPassword = $util->isMasterKeyEnabled();
144
-	}
145
-
146
-	/**
147
-	 * @return string defining the technical unique id
148
-	 */
149
-	public function getId() {
150
-		return self::ID;
151
-	}
152
-
153
-	/**
154
-	 * In comparison to getKey() this function returns a human readable (maybe translated) name
155
-	 *
156
-	 * @return string
157
-	 */
158
-	public function getDisplayName() {
159
-		return self::DISPLAY_NAME;
160
-	}
161
-
162
-	/**
163
-	 * start receiving chunks from a file. This is the place where you can
164
-	 * perform some initial step before starting encrypting/decrypting the
165
-	 * chunks
166
-	 *
167
-	 * @param string $path to the file
168
-	 * @param string $user who read/write the file
169
-	 * @param string $mode php stream open mode
170
-	 * @param array $header contains the header data read from the file
171
-	 * @param array $accessList who has access to the file contains the key 'users' and 'public'
172
-	 *
173
-	 * @return array $header contain data as key-value pairs which should be
174
-	 *                       written to the header, in case of a write operation
175
-	 *                       or if no additional data is needed return a empty array
176
-	 */
177
-	public function begin($path, $user, $mode, array $header, array $accessList) {
178
-		$this->path = $this->getPathToRealFile($path);
179
-		$this->accessList = $accessList;
180
-		$this->user = $user;
181
-		$this->isWriteOperation = false;
182
-		$this->writeCache = '';
183
-
184
-		if($this->session->isReady() === false) {
185
-			// if the master key is enabled we can initialize encryption
186
-			// with a empty password and user name
187
-			if ($this->util->isMasterKeyEnabled()) {
188
-				$this->keyManager->init('', '');
189
-			}
190
-		}
191
-
192
-		if ($this->session->decryptAllModeActivated()) {
193
-			$encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path);
194
-			$shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid());
195
-			$this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
196
-				$shareKey,
197
-				$this->session->getDecryptAllKey());
198
-		} else {
199
-			$this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
200
-		}
201
-
202
-		// always use the version from the original file, also part files
203
-		// need to have a correct version number if they get moved over to the
204
-		// final location
205
-		$this->version = (int)$this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
206
-
207
-		if (
208
-			$mode === 'w'
209
-			|| $mode === 'w+'
210
-			|| $mode === 'wb'
211
-			|| $mode === 'wb+'
212
-		) {
213
-			$this->isWriteOperation = true;
214
-			if (empty($this->fileKey)) {
215
-				$this->fileKey = $this->crypt->generateFileKey();
216
-			}
217
-		} else {
218
-			// if we read a part file we need to increase the version by 1
219
-			// because the version number was also increased by writing
220
-			// the part file
221
-			if(Scanner::isPartialFile($path)) {
222
-				$this->version = $this->version + 1;
223
-			}
224
-		}
225
-
226
-		if ($this->isWriteOperation) {
227
-			$this->cipher = $this->crypt->getCipher();
228
-		} elseif (isset($header['cipher'])) {
229
-			$this->cipher = $header['cipher'];
230
-		} else {
231
-			// if we read a file without a header we fall-back to the legacy cipher
232
-			// which was used in <=oC6
233
-			$this->cipher = $this->crypt->getLegacyCipher();
234
-		}
235
-
236
-		return array('cipher' => $this->cipher, 'signed' => 'true');
237
-	}
238
-
239
-	/**
240
-	 * last chunk received. This is the place where you can perform some final
241
-	 * operation and return some remaining data if something is left in your
242
-	 * buffer.
243
-	 *
244
-	 * @param string $path to the file
245
-	 * @param int $position
246
-	 * @return string remained data which should be written to the file in case
247
-	 *                of a write operation
248
-	 * @throws PublicKeyMissingException
249
-	 * @throws \Exception
250
-	 * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException
251
-	 */
252
-	public function end($path, $position = 0) {
253
-		$result = '';
254
-		if ($this->isWriteOperation) {
255
-			$this->keyManager->setVersion($path, $this->version + 1, new View());
256
-			// in case of a part file we remember the new signature versions
257
-			// the version will be set later on update.
258
-			// This way we make sure that other apps listening to the pre-hooks
259
-			// still get the old version which should be the correct value for them
260
-			if (Scanner::isPartialFile($path)) {
261
-				self::$rememberVersion[$this->stripPartFileExtension($path)] = $this->version + 1;
262
-			}
263
-			if (!empty($this->writeCache)) {
264
-				$result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey, $this->version + 1, $position);
265
-				$this->writeCache = '';
266
-			}
267
-			$publicKeys = array();
268
-			if ($this->useMasterPassword === true) {
269
-				$publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
270
-			} else {
271
-				foreach ($this->accessList['users'] as $uid) {
272
-					try {
273
-						$publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
274
-					} catch (PublicKeyMissingException $e) {
275
-						$this->logger->warning(
276
-							'no public key found for user "{uid}", user will not be able to read the file',
277
-							['app' => 'encryption', 'uid' => $uid]
278
-						);
279
-						// if the public key of the owner is missing we should fail
280
-						if ($uid === $this->user) {
281
-							throw $e;
282
-						}
283
-					}
284
-				}
285
-			}
286
-
287
-			$publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys, $this->getOwner($path));
288
-			$encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys);
289
-			$this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles);
290
-		}
291
-		return $result;
292
-	}
293
-
294
-
295
-
296
-	/**
297
-	 * encrypt data
298
-	 *
299
-	 * @param string $data you want to encrypt
300
-	 * @param int $position
301
-	 * @return string encrypted data
302
-	 */
303
-	public function encrypt($data, $position = 0) {
304
-		// If extra data is left over from the last round, make sure it
305
-		// is integrated into the next block
306
-		if ($this->writeCache) {
307
-
308
-			// Concat writeCache to start of $data
309
-			$data = $this->writeCache . $data;
310
-
311
-			// Clear the write cache, ready for reuse - it has been
312
-			// flushed and its old contents processed
313
-			$this->writeCache = '';
314
-
315
-		}
316
-
317
-		$encrypted = '';
318
-		// While there still remains some data to be processed & written
319
-		while (strlen($data) > 0) {
320
-
321
-			// Remaining length for this iteration, not of the
322
-			// entire file (may be greater than 8192 bytes)
323
-			$remainingLength = strlen($data);
324
-
325
-			// If data remaining to be written is less than the
326
-			// size of 1 6126 byte block
327
-			if ($remainingLength < $this->unencryptedBlockSizeSigned) {
328
-
329
-				// Set writeCache to contents of $data
330
-				// The writeCache will be carried over to the
331
-				// next write round, and added to the start of
332
-				// $data to ensure that written blocks are
333
-				// always the correct length. If there is still
334
-				// data in writeCache after the writing round
335
-				// has finished, then the data will be written
336
-				// to disk by $this->flush().
337
-				$this->writeCache = $data;
338
-
339
-				// Clear $data ready for next round
340
-				$data = '';
341
-
342
-			} else {
343
-
344
-				// Read the chunk from the start of $data
345
-				$chunk = substr($data, 0, $this->unencryptedBlockSizeSigned);
346
-
347
-				$encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, $position);
348
-
349
-				// Remove the chunk we just processed from
350
-				// $data, leaving only unprocessed data in $data
351
-				// var, for handling on the next round
352
-				$data = substr($data, $this->unencryptedBlockSizeSigned);
353
-
354
-			}
355
-
356
-		}
357
-
358
-		return $encrypted;
359
-	}
360
-
361
-	/**
362
-	 * decrypt data
363
-	 *
364
-	 * @param string $data you want to decrypt
365
-	 * @param int $position
366
-	 * @return string decrypted data
367
-	 * @throws DecryptionFailedException
368
-	 */
369
-	public function decrypt($data, $position = 0) {
370
-		if (empty($this->fileKey)) {
371
-			$msg = 'Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.';
372
-			$hint = $this->l->t('Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
373
-			$this->logger->error($msg);
374
-
375
-			throw new DecryptionFailedException($msg, $hint);
376
-		}
377
-
378
-		return $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher, $this->version, $position);
379
-	}
380
-
381
-	/**
382
-	 * update encrypted file, e.g. give additional users access to the file
383
-	 *
384
-	 * @param string $path path to the file which should be updated
385
-	 * @param string $uid of the user who performs the operation
386
-	 * @param array $accessList who has access to the file contains the key 'users' and 'public'
387
-	 * @return boolean
388
-	 */
389
-	public function update($path, $uid, array $accessList) {
390
-
391
-		if (empty($accessList)) {
392
-			if (isset(self::$rememberVersion[$path])) {
393
-				$this->keyManager->setVersion($path, self::$rememberVersion[$path], new View());
394
-				unset(self::$rememberVersion[$path]);
395
-			}
396
-			return;
397
-		}
398
-
399
-		$fileKey = $this->keyManager->getFileKey($path, $uid);
400
-
401
-		if (!empty($fileKey)) {
402
-
403
-			$publicKeys = array();
404
-			if ($this->useMasterPassword === true) {
405
-				$publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
406
-			} else {
407
-				foreach ($accessList['users'] as $user) {
408
-					try {
409
-						$publicKeys[$user] = $this->keyManager->getPublicKey($user);
410
-					} catch (PublicKeyMissingException $e) {
411
-						$this->logger->warning('Could not encrypt file for ' . $user . ': ' . $e->getMessage());
412
-					}
413
-				}
414
-			}
415
-
416
-			$publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->getOwner($path));
417
-
418
-			$encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
419
-
420
-			$this->keyManager->deleteAllFileKeys($path);
421
-
422
-			$this->keyManager->setAllFileKeys($path, $encryptedFileKey);
423
-
424
-		} else {
425
-			$this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted',
426
-				array('file' => $path, 'app' => 'encryption'));
427
-
428
-			return false;
429
-		}
430
-
431
-		return true;
432
-	}
433
-
434
-	/**
435
-	 * should the file be encrypted or not
436
-	 *
437
-	 * @param string $path
438
-	 * @return boolean
439
-	 */
440
-	public function shouldEncrypt($path) {
441
-		if ($this->util->shouldEncryptHomeStorage() === false) {
442
-			$storage = $this->util->getStorage($path);
443
-			if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
444
-				return false;
445
-			}
446
-		}
447
-		$parts = explode('/', $path);
448
-		if (count($parts) < 4) {
449
-			return false;
450
-		}
451
-
452
-		if ($parts[2] === 'files') {
453
-			return true;
454
-		}
455
-		if ($parts[2] === 'files_versions') {
456
-			return true;
457
-		}
458
-		if ($parts[2] === 'files_trashbin') {
459
-			return true;
460
-		}
461
-
462
-		return false;
463
-	}
464
-
465
-	/**
466
-	 * get size of the unencrypted payload per block.
467
-	 * Nextcloud read/write files with a block size of 8192 byte
468
-	 *
469
-	 * @param bool $signed
470
-	 * @return int
471
-	 */
472
-	public function getUnencryptedBlockSize($signed = false) {
473
-		if ($signed === false) {
474
-			return $this->unencryptedBlockSize;
475
-		}
476
-
477
-		return $this->unencryptedBlockSizeSigned;
478
-	}
479
-
480
-	/**
481
-	 * check if the encryption module is able to read the file,
482
-	 * e.g. if all encryption keys exists
483
-	 *
484
-	 * @param string $path
485
-	 * @param string $uid user for whom we want to check if he can read the file
486
-	 * @return bool
487
-	 * @throws DecryptionFailedException
488
-	 */
489
-	public function isReadable($path, $uid) {
490
-		$fileKey = $this->keyManager->getFileKey($path, $uid);
491
-		if (empty($fileKey)) {
492
-			$owner = $this->util->getOwner($path);
493
-			if ($owner !== $uid) {
494
-				// if it is a shared file we throw a exception with a useful
495
-				// error message because in this case it means that the file was
496
-				// shared with the user at a point where the user didn't had a
497
-				// valid private/public key
498
-				$msg = 'Encryption module "' . $this->getDisplayName() .
499
-					'" is not able to read ' . $path;
500
-				$hint = $this->l->t('Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
501
-				$this->logger->warning($msg);
502
-				throw new DecryptionFailedException($msg, $hint);
503
-			}
504
-			return false;
505
-		}
506
-
507
-		return true;
508
-	}
509
-
510
-	/**
511
-	 * Initial encryption of all files
512
-	 *
513
-	 * @param InputInterface $input
514
-	 * @param OutputInterface $output write some status information to the terminal during encryption
515
-	 */
516
-	public function encryptAll(InputInterface $input, OutputInterface $output) {
517
-		$this->encryptAll->encryptAll($input, $output);
518
-	}
519
-
520
-	/**
521
-	 * prepare module to perform decrypt all operation
522
-	 *
523
-	 * @param InputInterface $input
524
-	 * @param OutputInterface $output
525
-	 * @param string $user
526
-	 * @return bool
527
-	 */
528
-	public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '') {
529
-		return $this->decryptAll->prepare($input, $output, $user);
530
-	}
531
-
532
-
533
-	/**
534
-	 * @param string $path
535
-	 * @return string
536
-	 */
537
-	protected function getPathToRealFile($path) {
538
-		$realPath = $path;
539
-		$parts = explode('/', $path);
540
-		if ($parts[2] === 'files_versions') {
541
-			$realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
542
-			$length = strrpos($realPath, '.');
543
-			$realPath = substr($realPath, 0, $length);
544
-		}
545
-
546
-		return $realPath;
547
-	}
548
-
549
-	/**
550
-	 * remove .part file extension and the ocTransferId from the file to get the
551
-	 * original file name
552
-	 *
553
-	 * @param string $path
554
-	 * @return string
555
-	 */
556
-	protected function stripPartFileExtension($path) {
557
-		if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
558
-			$pos = strrpos($path, '.', -6);
559
-			$path = substr($path, 0, $pos);
560
-		}
561
-
562
-		return $path;
563
-	}
564
-
565
-	/**
566
-	 * get owner of a file
567
-	 *
568
-	 * @param string $path
569
-	 * @return string
570
-	 */
571
-	protected function getOwner($path) {
572
-		if (!isset($this->owner[$path])) {
573
-			$this->owner[$path] = $this->util->getOwner($path);
574
-		}
575
-		return $this->owner[$path];
576
-	}
577
-
578
-	/**
579
-	 * Check if the module is ready to be used by that specific user.
580
-	 * In case a module is not ready - because e.g. key pairs have not been generated
581
-	 * upon login this method can return false before any operation starts and might
582
-	 * cause issues during operations.
583
-	 *
584
-	 * @param string $user
585
-	 * @return boolean
586
-	 * @since 9.1.0
587
-	 */
588
-	public function isReadyForUser($user) {
589
-		return $this->keyManager->userHasKeys($user);
590
-	}
591
-
592
-	/**
593
-	 * We only need a detailed access list if the master key is not enabled
594
-	 *
595
-	 * @return bool
596
-	 */
597
-	public function needDetailedAccessList() {
598
-		return !$this->util->isMasterKeyEnabled();
599
-	}
46
+    const ID = 'OC_DEFAULT_MODULE';
47
+    const DISPLAY_NAME = 'Default encryption module';
48
+
49
+    /**
50
+     * @var Crypt
51
+     */
52
+    private $crypt;
53
+
54
+    /** @var string */
55
+    private $cipher;
56
+
57
+    /** @var string */
58
+    private $path;
59
+
60
+    /** @var string */
61
+    private $user;
62
+
63
+    /** @var  array */
64
+    private $owner;
65
+
66
+    /** @var string */
67
+    private $fileKey;
68
+
69
+    /** @var string */
70
+    private $writeCache;
71
+
72
+    /** @var KeyManager */
73
+    private $keyManager;
74
+
75
+    /** @var array */
76
+    private $accessList;
77
+
78
+    /** @var boolean */
79
+    private $isWriteOperation;
80
+
81
+    /** @var Util */
82
+    private $util;
83
+
84
+    /** @var  Session */
85
+    private $session;
86
+
87
+    /** @var  ILogger */
88
+    private $logger;
89
+
90
+    /** @var IL10N */
91
+    private $l;
92
+
93
+    /** @var EncryptAll */
94
+    private $encryptAll;
95
+
96
+    /** @var  bool */
97
+    private $useMasterPassword;
98
+
99
+    /** @var DecryptAll  */
100
+    private $decryptAll;
101
+
102
+    /** @var int unencrypted block size if block contains signature */
103
+    private $unencryptedBlockSizeSigned = 6072;
104
+
105
+    /** @var int unencrypted block size */
106
+    private $unencryptedBlockSize = 6126;
107
+
108
+    /** @var int Current version of the file */
109
+    private $version = 0;
110
+
111
+    /** @var array remember encryption signature version */
112
+    private static $rememberVersion = [];
113
+
114
+
115
+    /**
116
+     *
117
+     * @param Crypt $crypt
118
+     * @param KeyManager $keyManager
119
+     * @param Util $util
120
+     * @param Session $session
121
+     * @param EncryptAll $encryptAll
122
+     * @param DecryptAll $decryptAll
123
+     * @param ILogger $logger
124
+     * @param IL10N $il10n
125
+     */
126
+    public function __construct(Crypt $crypt,
127
+                                KeyManager $keyManager,
128
+                                Util $util,
129
+                                Session $session,
130
+                                EncryptAll $encryptAll,
131
+                                DecryptAll $decryptAll,
132
+                                ILogger $logger,
133
+                                IL10N $il10n) {
134
+        $this->crypt = $crypt;
135
+        $this->keyManager = $keyManager;
136
+        $this->util = $util;
137
+        $this->session = $session;
138
+        $this->encryptAll = $encryptAll;
139
+        $this->decryptAll = $decryptAll;
140
+        $this->logger = $logger;
141
+        $this->l = $il10n;
142
+        $this->owner = [];
143
+        $this->useMasterPassword = $util->isMasterKeyEnabled();
144
+    }
145
+
146
+    /**
147
+     * @return string defining the technical unique id
148
+     */
149
+    public function getId() {
150
+        return self::ID;
151
+    }
152
+
153
+    /**
154
+     * In comparison to getKey() this function returns a human readable (maybe translated) name
155
+     *
156
+     * @return string
157
+     */
158
+    public function getDisplayName() {
159
+        return self::DISPLAY_NAME;
160
+    }
161
+
162
+    /**
163
+     * start receiving chunks from a file. This is the place where you can
164
+     * perform some initial step before starting encrypting/decrypting the
165
+     * chunks
166
+     *
167
+     * @param string $path to the file
168
+     * @param string $user who read/write the file
169
+     * @param string $mode php stream open mode
170
+     * @param array $header contains the header data read from the file
171
+     * @param array $accessList who has access to the file contains the key 'users' and 'public'
172
+     *
173
+     * @return array $header contain data as key-value pairs which should be
174
+     *                       written to the header, in case of a write operation
175
+     *                       or if no additional data is needed return a empty array
176
+     */
177
+    public function begin($path, $user, $mode, array $header, array $accessList) {
178
+        $this->path = $this->getPathToRealFile($path);
179
+        $this->accessList = $accessList;
180
+        $this->user = $user;
181
+        $this->isWriteOperation = false;
182
+        $this->writeCache = '';
183
+
184
+        if($this->session->isReady() === false) {
185
+            // if the master key is enabled we can initialize encryption
186
+            // with a empty password and user name
187
+            if ($this->util->isMasterKeyEnabled()) {
188
+                $this->keyManager->init('', '');
189
+            }
190
+        }
191
+
192
+        if ($this->session->decryptAllModeActivated()) {
193
+            $encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path);
194
+            $shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid());
195
+            $this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
196
+                $shareKey,
197
+                $this->session->getDecryptAllKey());
198
+        } else {
199
+            $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
200
+        }
201
+
202
+        // always use the version from the original file, also part files
203
+        // need to have a correct version number if they get moved over to the
204
+        // final location
205
+        $this->version = (int)$this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
206
+
207
+        if (
208
+            $mode === 'w'
209
+            || $mode === 'w+'
210
+            || $mode === 'wb'
211
+            || $mode === 'wb+'
212
+        ) {
213
+            $this->isWriteOperation = true;
214
+            if (empty($this->fileKey)) {
215
+                $this->fileKey = $this->crypt->generateFileKey();
216
+            }
217
+        } else {
218
+            // if we read a part file we need to increase the version by 1
219
+            // because the version number was also increased by writing
220
+            // the part file
221
+            if(Scanner::isPartialFile($path)) {
222
+                $this->version = $this->version + 1;
223
+            }
224
+        }
225
+
226
+        if ($this->isWriteOperation) {
227
+            $this->cipher = $this->crypt->getCipher();
228
+        } elseif (isset($header['cipher'])) {
229
+            $this->cipher = $header['cipher'];
230
+        } else {
231
+            // if we read a file without a header we fall-back to the legacy cipher
232
+            // which was used in <=oC6
233
+            $this->cipher = $this->crypt->getLegacyCipher();
234
+        }
235
+
236
+        return array('cipher' => $this->cipher, 'signed' => 'true');
237
+    }
238
+
239
+    /**
240
+     * last chunk received. This is the place where you can perform some final
241
+     * operation and return some remaining data if something is left in your
242
+     * buffer.
243
+     *
244
+     * @param string $path to the file
245
+     * @param int $position
246
+     * @return string remained data which should be written to the file in case
247
+     *                of a write operation
248
+     * @throws PublicKeyMissingException
249
+     * @throws \Exception
250
+     * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException
251
+     */
252
+    public function end($path, $position = 0) {
253
+        $result = '';
254
+        if ($this->isWriteOperation) {
255
+            $this->keyManager->setVersion($path, $this->version + 1, new View());
256
+            // in case of a part file we remember the new signature versions
257
+            // the version will be set later on update.
258
+            // This way we make sure that other apps listening to the pre-hooks
259
+            // still get the old version which should be the correct value for them
260
+            if (Scanner::isPartialFile($path)) {
261
+                self::$rememberVersion[$this->stripPartFileExtension($path)] = $this->version + 1;
262
+            }
263
+            if (!empty($this->writeCache)) {
264
+                $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey, $this->version + 1, $position);
265
+                $this->writeCache = '';
266
+            }
267
+            $publicKeys = array();
268
+            if ($this->useMasterPassword === true) {
269
+                $publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
270
+            } else {
271
+                foreach ($this->accessList['users'] as $uid) {
272
+                    try {
273
+                        $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
274
+                    } catch (PublicKeyMissingException $e) {
275
+                        $this->logger->warning(
276
+                            'no public key found for user "{uid}", user will not be able to read the file',
277
+                            ['app' => 'encryption', 'uid' => $uid]
278
+                        );
279
+                        // if the public key of the owner is missing we should fail
280
+                        if ($uid === $this->user) {
281
+                            throw $e;
282
+                        }
283
+                    }
284
+                }
285
+            }
286
+
287
+            $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys, $this->getOwner($path));
288
+            $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys);
289
+            $this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles);
290
+        }
291
+        return $result;
292
+    }
293
+
294
+
295
+
296
+    /**
297
+     * encrypt data
298
+     *
299
+     * @param string $data you want to encrypt
300
+     * @param int $position
301
+     * @return string encrypted data
302
+     */
303
+    public function encrypt($data, $position = 0) {
304
+        // If extra data is left over from the last round, make sure it
305
+        // is integrated into the next block
306
+        if ($this->writeCache) {
307
+
308
+            // Concat writeCache to start of $data
309
+            $data = $this->writeCache . $data;
310
+
311
+            // Clear the write cache, ready for reuse - it has been
312
+            // flushed and its old contents processed
313
+            $this->writeCache = '';
314
+
315
+        }
316
+
317
+        $encrypted = '';
318
+        // While there still remains some data to be processed & written
319
+        while (strlen($data) > 0) {
320
+
321
+            // Remaining length for this iteration, not of the
322
+            // entire file (may be greater than 8192 bytes)
323
+            $remainingLength = strlen($data);
324
+
325
+            // If data remaining to be written is less than the
326
+            // size of 1 6126 byte block
327
+            if ($remainingLength < $this->unencryptedBlockSizeSigned) {
328
+
329
+                // Set writeCache to contents of $data
330
+                // The writeCache will be carried over to the
331
+                // next write round, and added to the start of
332
+                // $data to ensure that written blocks are
333
+                // always the correct length. If there is still
334
+                // data in writeCache after the writing round
335
+                // has finished, then the data will be written
336
+                // to disk by $this->flush().
337
+                $this->writeCache = $data;
338
+
339
+                // Clear $data ready for next round
340
+                $data = '';
341
+
342
+            } else {
343
+
344
+                // Read the chunk from the start of $data
345
+                $chunk = substr($data, 0, $this->unencryptedBlockSizeSigned);
346
+
347
+                $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, $position);
348
+
349
+                // Remove the chunk we just processed from
350
+                // $data, leaving only unprocessed data in $data
351
+                // var, for handling on the next round
352
+                $data = substr($data, $this->unencryptedBlockSizeSigned);
353
+
354
+            }
355
+
356
+        }
357
+
358
+        return $encrypted;
359
+    }
360
+
361
+    /**
362
+     * decrypt data
363
+     *
364
+     * @param string $data you want to decrypt
365
+     * @param int $position
366
+     * @return string decrypted data
367
+     * @throws DecryptionFailedException
368
+     */
369
+    public function decrypt($data, $position = 0) {
370
+        if (empty($this->fileKey)) {
371
+            $msg = 'Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.';
372
+            $hint = $this->l->t('Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
373
+            $this->logger->error($msg);
374
+
375
+            throw new DecryptionFailedException($msg, $hint);
376
+        }
377
+
378
+        return $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher, $this->version, $position);
379
+    }
380
+
381
+    /**
382
+     * update encrypted file, e.g. give additional users access to the file
383
+     *
384
+     * @param string $path path to the file which should be updated
385
+     * @param string $uid of the user who performs the operation
386
+     * @param array $accessList who has access to the file contains the key 'users' and 'public'
387
+     * @return boolean
388
+     */
389
+    public function update($path, $uid, array $accessList) {
390
+
391
+        if (empty($accessList)) {
392
+            if (isset(self::$rememberVersion[$path])) {
393
+                $this->keyManager->setVersion($path, self::$rememberVersion[$path], new View());
394
+                unset(self::$rememberVersion[$path]);
395
+            }
396
+            return;
397
+        }
398
+
399
+        $fileKey = $this->keyManager->getFileKey($path, $uid);
400
+
401
+        if (!empty($fileKey)) {
402
+
403
+            $publicKeys = array();
404
+            if ($this->useMasterPassword === true) {
405
+                $publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
406
+            } else {
407
+                foreach ($accessList['users'] as $user) {
408
+                    try {
409
+                        $publicKeys[$user] = $this->keyManager->getPublicKey($user);
410
+                    } catch (PublicKeyMissingException $e) {
411
+                        $this->logger->warning('Could not encrypt file for ' . $user . ': ' . $e->getMessage());
412
+                    }
413
+                }
414
+            }
415
+
416
+            $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->getOwner($path));
417
+
418
+            $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
419
+
420
+            $this->keyManager->deleteAllFileKeys($path);
421
+
422
+            $this->keyManager->setAllFileKeys($path, $encryptedFileKey);
423
+
424
+        } else {
425
+            $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted',
426
+                array('file' => $path, 'app' => 'encryption'));
427
+
428
+            return false;
429
+        }
430
+
431
+        return true;
432
+    }
433
+
434
+    /**
435
+     * should the file be encrypted or not
436
+     *
437
+     * @param string $path
438
+     * @return boolean
439
+     */
440
+    public function shouldEncrypt($path) {
441
+        if ($this->util->shouldEncryptHomeStorage() === false) {
442
+            $storage = $this->util->getStorage($path);
443
+            if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
444
+                return false;
445
+            }
446
+        }
447
+        $parts = explode('/', $path);
448
+        if (count($parts) < 4) {
449
+            return false;
450
+        }
451
+
452
+        if ($parts[2] === 'files') {
453
+            return true;
454
+        }
455
+        if ($parts[2] === 'files_versions') {
456
+            return true;
457
+        }
458
+        if ($parts[2] === 'files_trashbin') {
459
+            return true;
460
+        }
461
+
462
+        return false;
463
+    }
464
+
465
+    /**
466
+     * get size of the unencrypted payload per block.
467
+     * Nextcloud read/write files with a block size of 8192 byte
468
+     *
469
+     * @param bool $signed
470
+     * @return int
471
+     */
472
+    public function getUnencryptedBlockSize($signed = false) {
473
+        if ($signed === false) {
474
+            return $this->unencryptedBlockSize;
475
+        }
476
+
477
+        return $this->unencryptedBlockSizeSigned;
478
+    }
479
+
480
+    /**
481
+     * check if the encryption module is able to read the file,
482
+     * e.g. if all encryption keys exists
483
+     *
484
+     * @param string $path
485
+     * @param string $uid user for whom we want to check if he can read the file
486
+     * @return bool
487
+     * @throws DecryptionFailedException
488
+     */
489
+    public function isReadable($path, $uid) {
490
+        $fileKey = $this->keyManager->getFileKey($path, $uid);
491
+        if (empty($fileKey)) {
492
+            $owner = $this->util->getOwner($path);
493
+            if ($owner !== $uid) {
494
+                // if it is a shared file we throw a exception with a useful
495
+                // error message because in this case it means that the file was
496
+                // shared with the user at a point where the user didn't had a
497
+                // valid private/public key
498
+                $msg = 'Encryption module "' . $this->getDisplayName() .
499
+                    '" is not able to read ' . $path;
500
+                $hint = $this->l->t('Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you.');
501
+                $this->logger->warning($msg);
502
+                throw new DecryptionFailedException($msg, $hint);
503
+            }
504
+            return false;
505
+        }
506
+
507
+        return true;
508
+    }
509
+
510
+    /**
511
+     * Initial encryption of all files
512
+     *
513
+     * @param InputInterface $input
514
+     * @param OutputInterface $output write some status information to the terminal during encryption
515
+     */
516
+    public function encryptAll(InputInterface $input, OutputInterface $output) {
517
+        $this->encryptAll->encryptAll($input, $output);
518
+    }
519
+
520
+    /**
521
+     * prepare module to perform decrypt all operation
522
+     *
523
+     * @param InputInterface $input
524
+     * @param OutputInterface $output
525
+     * @param string $user
526
+     * @return bool
527
+     */
528
+    public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '') {
529
+        return $this->decryptAll->prepare($input, $output, $user);
530
+    }
531
+
532
+
533
+    /**
534
+     * @param string $path
535
+     * @return string
536
+     */
537
+    protected function getPathToRealFile($path) {
538
+        $realPath = $path;
539
+        $parts = explode('/', $path);
540
+        if ($parts[2] === 'files_versions') {
541
+            $realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
542
+            $length = strrpos($realPath, '.');
543
+            $realPath = substr($realPath, 0, $length);
544
+        }
545
+
546
+        return $realPath;
547
+    }
548
+
549
+    /**
550
+     * remove .part file extension and the ocTransferId from the file to get the
551
+     * original file name
552
+     *
553
+     * @param string $path
554
+     * @return string
555
+     */
556
+    protected function stripPartFileExtension($path) {
557
+        if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
558
+            $pos = strrpos($path, '.', -6);
559
+            $path = substr($path, 0, $pos);
560
+        }
561
+
562
+        return $path;
563
+    }
564
+
565
+    /**
566
+     * get owner of a file
567
+     *
568
+     * @param string $path
569
+     * @return string
570
+     */
571
+    protected function getOwner($path) {
572
+        if (!isset($this->owner[$path])) {
573
+            $this->owner[$path] = $this->util->getOwner($path);
574
+        }
575
+        return $this->owner[$path];
576
+    }
577
+
578
+    /**
579
+     * Check if the module is ready to be used by that specific user.
580
+     * In case a module is not ready - because e.g. key pairs have not been generated
581
+     * upon login this method can return false before any operation starts and might
582
+     * cause issues during operations.
583
+     *
584
+     * @param string $user
585
+     * @return boolean
586
+     * @since 9.1.0
587
+     */
588
+    public function isReadyForUser($user) {
589
+        return $this->keyManager->userHasKeys($user);
590
+    }
591
+
592
+    /**
593
+     * We only need a detailed access list if the master key is not enabled
594
+     *
595
+     * @return bool
596
+     */
597
+    public function needDetailedAccessList() {
598
+        return !$this->util->isMasterKeyEnabled();
599
+    }
600 600
 }
Please login to merge, or discard this patch.
apps/encryption/lib/KeyManager.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -488,7 +488,7 @@
 block discarded – undo
488 488
 
489 489
 
490 490
 	/**
491
-	 * @param $path
491
+	 * @param string $path
492 492
 	 * @param $uid
493 493
 	 * @return mixed
494 494
 	 */
Please login to merge, or discard this patch.
Indentation   +681 added lines, -681 removed lines patch added patch discarded remove patch
@@ -40,685 +40,685 @@
 block discarded – undo
40 40
 
41 41
 class KeyManager {
42 42
 
43
-	/**
44
-	 * @var Session
45
-	 */
46
-	protected $session;
47
-	/**
48
-	 * @var IStorage
49
-	 */
50
-	private $keyStorage;
51
-	/**
52
-	 * @var Crypt
53
-	 */
54
-	private $crypt;
55
-	/**
56
-	 * @var string
57
-	 */
58
-	private $recoveryKeyId;
59
-	/**
60
-	 * @var string
61
-	 */
62
-	private $publicShareKeyId;
63
-	/**
64
-	 * @var string
65
-	 */
66
-	private $masterKeyId;
67
-	/**
68
-	 * @var string UserID
69
-	 */
70
-	private $keyId;
71
-	/**
72
-	 * @var string
73
-	 */
74
-	private $publicKeyId = 'publicKey';
75
-	/**
76
-	 * @var string
77
-	 */
78
-	private $privateKeyId = 'privateKey';
79
-
80
-	/**
81
-	 * @var string
82
-	 */
83
-	private $shareKeyId = 'shareKey';
84
-
85
-	/**
86
-	 * @var string
87
-	 */
88
-	private $fileKeyId = 'fileKey';
89
-	/**
90
-	 * @var IConfig
91
-	 */
92
-	private $config;
93
-	/**
94
-	 * @var ILogger
95
-	 */
96
-	private $log;
97
-	/**
98
-	 * @var Util
99
-	 */
100
-	private $util;
101
-
102
-	/**
103
-	 * @param IStorage $keyStorage
104
-	 * @param Crypt $crypt
105
-	 * @param IConfig $config
106
-	 * @param IUserSession $userSession
107
-	 * @param Session $session
108
-	 * @param ILogger $log
109
-	 * @param Util $util
110
-	 */
111
-	public function __construct(
112
-		IStorage $keyStorage,
113
-		Crypt $crypt,
114
-		IConfig $config,
115
-		IUserSession $userSession,
116
-		Session $session,
117
-		ILogger $log,
118
-		Util $util
119
-	) {
120
-
121
-		$this->util = $util;
122
-		$this->session = $session;
123
-		$this->keyStorage = $keyStorage;
124
-		$this->crypt = $crypt;
125
-		$this->config = $config;
126
-		$this->log = $log;
127
-
128
-		$this->recoveryKeyId = $this->config->getAppValue('encryption',
129
-			'recoveryKeyId');
130
-		if (empty($this->recoveryKeyId)) {
131
-			$this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
132
-			$this->config->setAppValue('encryption',
133
-				'recoveryKeyId',
134
-				$this->recoveryKeyId);
135
-		}
136
-
137
-		$this->publicShareKeyId = $this->config->getAppValue('encryption',
138
-			'publicShareKeyId');
139
-		if (empty($this->publicShareKeyId)) {
140
-			$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
141
-			$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
142
-		}
143
-
144
-		$this->masterKeyId = $this->config->getAppValue('encryption',
145
-			'masterKeyId');
146
-		if (empty($this->masterKeyId)) {
147
-			$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
148
-			$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
149
-		}
150
-
151
-		$this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
152
-		$this->log = $log;
153
-	}
154
-
155
-	/**
156
-	 * check if key pair for public link shares exists, if not we create one
157
-	 */
158
-	public function validateShareKey() {
159
-		$shareKey = $this->getPublicShareKey();
160
-		if (empty($shareKey)) {
161
-			$keyPair = $this->crypt->createKeyPair();
162
-
163
-			// Save public key
164
-			$this->keyStorage->setSystemUserKey(
165
-				$this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
166
-				Encryption::ID);
167
-
168
-			// Encrypt private key empty passphrase
169
-			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
170
-			$header = $this->crypt->generateHeader();
171
-			$this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
172
-		}
173
-	}
174
-
175
-	/**
176
-	 * check if a key pair for the master key exists, if not we create one
177
-	 */
178
-	public function validateMasterKey() {
179
-
180
-		if ($this->util->isMasterKeyEnabled() === false) {
181
-			return;
182
-		}
183
-
184
-		$publicMasterKey = $this->getPublicMasterKey();
185
-		if (empty($publicMasterKey)) {
186
-			$keyPair = $this->crypt->createKeyPair();
187
-
188
-			// Save public key
189
-			$this->keyStorage->setSystemUserKey(
190
-				$this->masterKeyId . '.publicKey', $keyPair['publicKey'],
191
-				Encryption::ID);
192
-
193
-			// Encrypt private key with system password
194
-			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
195
-			$header = $this->crypt->generateHeader();
196
-			$this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
197
-		}
198
-
199
-		if (!$this->session->isPrivateKeySet()) {
200
-			$masterKey = $this->getSystemPrivateKey($this->masterKeyId);
201
-			$decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId);
202
-			$this->session->setPrivateKey($decryptedMasterKey);
203
-		}
204
-
205
-		// after the encryption key is available we are ready to go
206
-		$this->session->setStatus(Session::INIT_SUCCESSFUL);
207
-	}
208
-
209
-	/**
210
-	 * @return bool
211
-	 */
212
-	public function recoveryKeyExists() {
213
-		$key = $this->getRecoveryKey();
214
-		return !empty($key);
215
-	}
216
-
217
-	/**
218
-	 * get recovery key
219
-	 *
220
-	 * @return string
221
-	 */
222
-	public function getRecoveryKey() {
223
-		return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
224
-	}
225
-
226
-	/**
227
-	 * get recovery key ID
228
-	 *
229
-	 * @return string
230
-	 */
231
-	public function getRecoveryKeyId() {
232
-		return $this->recoveryKeyId;
233
-	}
234
-
235
-	/**
236
-	 * @param string $password
237
-	 * @return bool
238
-	 */
239
-	public function checkRecoveryPassword($password) {
240
-		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
241
-		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
242
-
243
-		if ($decryptedRecoveryKey) {
244
-			return true;
245
-		}
246
-		return false;
247
-	}
248
-
249
-	/**
250
-	 * @param string $uid
251
-	 * @param string $password
252
-	 * @param string $keyPair
253
-	 * @return bool
254
-	 */
255
-	public function storeKeyPair($uid, $password, $keyPair) {
256
-		// Save Public Key
257
-		$this->setPublicKey($uid, $keyPair['publicKey']);
258
-
259
-		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid);
260
-
261
-		$header = $this->crypt->generateHeader();
262
-
263
-		if ($encryptedKey) {
264
-			$this->setPrivateKey($uid, $header . $encryptedKey);
265
-			return true;
266
-		}
267
-		return false;
268
-	}
269
-
270
-	/**
271
-	 * @param string $password
272
-	 * @param array $keyPair
273
-	 * @return bool
274
-	 */
275
-	public function setRecoveryKey($password, $keyPair) {
276
-		// Save Public Key
277
-		$this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
278
-			'.publicKey',
279
-			$keyPair['publicKey'],
280
-			Encryption::ID);
281
-
282
-		$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
283
-		$header = $this->crypt->generateHeader();
284
-
285
-		if ($encryptedKey) {
286
-			$this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
287
-			return true;
288
-		}
289
-		return false;
290
-	}
291
-
292
-	/**
293
-	 * @param $userId
294
-	 * @param $key
295
-	 * @return bool
296
-	 */
297
-	public function setPublicKey($userId, $key) {
298
-		return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID);
299
-	}
300
-
301
-	/**
302
-	 * @param $userId
303
-	 * @param string $key
304
-	 * @return bool
305
-	 */
306
-	public function setPrivateKey($userId, $key) {
307
-		return $this->keyStorage->setUserKey($userId,
308
-			$this->privateKeyId,
309
-			$key,
310
-			Encryption::ID);
311
-	}
312
-
313
-	/**
314
-	 * write file key to key storage
315
-	 *
316
-	 * @param string $path
317
-	 * @param string $key
318
-	 * @return boolean
319
-	 */
320
-	public function setFileKey($path, $key) {
321
-		return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID);
322
-	}
323
-
324
-	/**
325
-	 * set all file keys (the file key and the corresponding share keys)
326
-	 *
327
-	 * @param string $path
328
-	 * @param array $keys
329
-	 */
330
-	public function setAllFileKeys($path, $keys) {
331
-		$this->setFileKey($path, $keys['data']);
332
-		foreach ($keys['keys'] as $uid => $keyFile) {
333
-			$this->setShareKey($path, $uid, $keyFile);
334
-		}
335
-	}
336
-
337
-	/**
338
-	 * write share key to the key storage
339
-	 *
340
-	 * @param string $path
341
-	 * @param string $uid
342
-	 * @param string $key
343
-	 * @return boolean
344
-	 */
345
-	public function setShareKey($path, $uid, $key) {
346
-		$keyId = $uid . '.' . $this->shareKeyId;
347
-		return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
348
-	}
349
-
350
-	/**
351
-	 * Decrypt private key and store it
352
-	 *
353
-	 * @param string $uid user id
354
-	 * @param string $passPhrase users password
355
-	 * @return boolean
356
-	 */
357
-	public function init($uid, $passPhrase) {
358
-
359
-		$this->session->setStatus(Session::INIT_EXECUTED);
360
-
361
-		try {
362
-			if($this->util->isMasterKeyEnabled()) {
363
-				$uid = $this->getMasterKeyId();
364
-				$passPhrase = $this->getMasterKeyPassword();
365
-				$privateKey = $this->getSystemPrivateKey($uid);
366
-			} else {
367
-				$privateKey = $this->getPrivateKey($uid);
368
-			}
369
-			$privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid);
370
-		} catch (PrivateKeyMissingException $e) {
371
-			return false;
372
-		} catch (DecryptionFailedException $e) {
373
-			return false;
374
-		} catch (\Exception $e) {
375
-			$this->log->logException($e, [
376
-				'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.',
377
-				'level' => ILogger::WARN,
378
-				'app' => 'encryption',
379
-			]);
380
-			return false;
381
-		}
382
-
383
-		if ($privateKey) {
384
-			$this->session->setPrivateKey($privateKey);
385
-			$this->session->setStatus(Session::INIT_SUCCESSFUL);
386
-			return true;
387
-		}
388
-
389
-		return false;
390
-	}
391
-
392
-	/**
393
-	 * @param $userId
394
-	 * @return string
395
-	 * @throws PrivateKeyMissingException
396
-	 */
397
-	public function getPrivateKey($userId) {
398
-		$privateKey = $this->keyStorage->getUserKey($userId,
399
-			$this->privateKeyId, Encryption::ID);
400
-
401
-		if (strlen($privateKey) !== 0) {
402
-			return $privateKey;
403
-		}
404
-		throw new PrivateKeyMissingException($userId);
405
-	}
406
-
407
-	/**
408
-	 * @param string $path
409
-	 * @param $uid
410
-	 * @return string
411
-	 */
412
-	public function getFileKey($path, $uid) {
413
-		if ($uid === '') {
414
-			$uid = null;
415
-		}
416
-		$publicAccess = is_null($uid);
417
-		$encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID);
418
-
419
-		if (empty($encryptedFileKey)) {
420
-			return '';
421
-		}
422
-
423
-		if ($this->util->isMasterKeyEnabled()) {
424
-			$uid = $this->getMasterKeyId();
425
-			$shareKey = $this->getShareKey($path, $uid);
426
-			if ($publicAccess) {
427
-				$privateKey = $this->getSystemPrivateKey($uid);
428
-				$privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid);
429
-			} else {
430
-				// when logged in, the master key is already decrypted in the session
431
-				$privateKey = $this->session->getPrivateKey();
432
-			}
433
-		} else if ($publicAccess) {
434
-			// use public share key for public links
435
-			$uid = $this->getPublicShareKeyId();
436
-			$shareKey = $this->getShareKey($path, $uid);
437
-			$privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
438
-			$privateKey = $this->crypt->decryptPrivateKey($privateKey);
439
-		} else {
440
-			$shareKey = $this->getShareKey($path, $uid);
441
-			$privateKey = $this->session->getPrivateKey();
442
-		}
443
-
444
-		if ($encryptedFileKey && $shareKey && $privateKey) {
445
-			return $this->crypt->multiKeyDecrypt($encryptedFileKey,
446
-				$shareKey,
447
-				$privateKey);
448
-		}
449
-
450
-		return '';
451
-	}
452
-
453
-	/**
454
-	 * Get the current version of a file
455
-	 *
456
-	 * @param string $path
457
-	 * @param View $view
458
-	 * @return int
459
-	 */
460
-	public function getVersion($path, View $view) {
461
-		$fileInfo = $view->getFileInfo($path);
462
-		if($fileInfo === false) {
463
-			return 0;
464
-		}
465
-		return $fileInfo->getEncryptedVersion();
466
-	}
467
-
468
-	/**
469
-	 * Set the current version of a file
470
-	 *
471
-	 * @param string $path
472
-	 * @param int $version
473
-	 * @param View $view
474
-	 */
475
-	public function setVersion($path, $version, View $view) {
476
-		$fileInfo= $view->getFileInfo($path);
477
-
478
-		if($fileInfo !== false) {
479
-			$cache = $fileInfo->getStorage()->getCache();
480
-			$cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]);
481
-		}
482
-	}
483
-
484
-	/**
485
-	 * get the encrypted file key
486
-	 *
487
-	 * @param string $path
488
-	 * @return string
489
-	 */
490
-	public function getEncryptedFileKey($path) {
491
-		$encryptedFileKey = $this->keyStorage->getFileKey($path,
492
-			$this->fileKeyId, Encryption::ID);
493
-
494
-		return $encryptedFileKey;
495
-	}
496
-
497
-	/**
498
-	 * delete share key
499
-	 *
500
-	 * @param string $path
501
-	 * @param string $keyId
502
-	 * @return boolean
503
-	 */
504
-	public function deleteShareKey($path, $keyId) {
505
-		return $this->keyStorage->deleteFileKey(
506
-			$path,
507
-			$keyId . '.' . $this->shareKeyId,
508
-			Encryption::ID);
509
-	}
510
-
511
-
512
-	/**
513
-	 * @param $path
514
-	 * @param $uid
515
-	 * @return mixed
516
-	 */
517
-	public function getShareKey($path, $uid) {
518
-		$keyId = $uid . '.' . $this->shareKeyId;
519
-		return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
520
-	}
521
-
522
-	/**
523
-	 * check if user has a private and a public key
524
-	 *
525
-	 * @param string $userId
526
-	 * @return bool
527
-	 * @throws PrivateKeyMissingException
528
-	 * @throws PublicKeyMissingException
529
-	 */
530
-	public function userHasKeys($userId) {
531
-		$privateKey = $publicKey = true;
532
-		$exception = null;
533
-
534
-		try {
535
-			$this->getPrivateKey($userId);
536
-		} catch (PrivateKeyMissingException $e) {
537
-			$privateKey = false;
538
-			$exception = $e;
539
-		}
540
-		try {
541
-			$this->getPublicKey($userId);
542
-		} catch (PublicKeyMissingException $e) {
543
-			$publicKey = false;
544
-			$exception = $e;
545
-		}
546
-
547
-		if ($privateKey && $publicKey) {
548
-			return true;
549
-		} elseif (!$privateKey && !$publicKey) {
550
-			return false;
551
-		} else {
552
-			throw $exception;
553
-		}
554
-	}
555
-
556
-	/**
557
-	 * @param $userId
558
-	 * @return mixed
559
-	 * @throws PublicKeyMissingException
560
-	 */
561
-	public function getPublicKey($userId) {
562
-		$publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID);
563
-
564
-		if (strlen($publicKey) !== 0) {
565
-			return $publicKey;
566
-		}
567
-		throw new PublicKeyMissingException($userId);
568
-	}
569
-
570
-	public function getPublicShareKeyId() {
571
-		return $this->publicShareKeyId;
572
-	}
573
-
574
-	/**
575
-	 * get public key for public link shares
576
-	 *
577
-	 * @return string
578
-	 */
579
-	public function getPublicShareKey() {
580
-		return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
581
-	}
582
-
583
-	/**
584
-	 * @param string $purpose
585
-	 * @param string $uid
586
-	 */
587
-	public function backupUserKeys($purpose, $uid) {
588
-		$this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid);
589
-	}
590
-
591
-	/**
592
-	 * creat a backup of the users private and public key and then  delete it
593
-	 *
594
-	 * @param string $uid
595
-	 */
596
-	public function deleteUserKeys($uid) {
597
-		$this->deletePublicKey($uid);
598
-		$this->deletePrivateKey($uid);
599
-	}
600
-
601
-	/**
602
-	 * @param $uid
603
-	 * @return bool
604
-	 */
605
-	public function deletePublicKey($uid) {
606
-		return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID);
607
-	}
608
-
609
-	/**
610
-	 * @param string $uid
611
-	 * @return bool
612
-	 */
613
-	private function deletePrivateKey($uid) {
614
-		return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID);
615
-	}
616
-
617
-	/**
618
-	 * @param string $path
619
-	 * @return bool
620
-	 */
621
-	public function deleteAllFileKeys($path) {
622
-		return $this->keyStorage->deleteAllFileKeys($path);
623
-	}
624
-
625
-	/**
626
-	 * @param array $userIds
627
-	 * @return array
628
-	 * @throws PublicKeyMissingException
629
-	 */
630
-	public function getPublicKeys(array $userIds) {
631
-		$keys = [];
632
-
633
-		foreach ($userIds as $userId) {
634
-			try {
635
-				$keys[$userId] = $this->getPublicKey($userId);
636
-			} catch (PublicKeyMissingException $e) {
637
-				continue;
638
-			}
639
-		}
640
-
641
-		return $keys;
642
-
643
-	}
644
-
645
-	/**
646
-	 * @param string $keyId
647
-	 * @return string returns openssl key
648
-	 */
649
-	public function getSystemPrivateKey($keyId) {
650
-		return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
651
-	}
652
-
653
-	/**
654
-	 * @param string $keyId
655
-	 * @param string $key
656
-	 * @return string returns openssl key
657
-	 */
658
-	public function setSystemPrivateKey($keyId, $key) {
659
-		return $this->keyStorage->setSystemUserKey(
660
-			$keyId . '.' . $this->privateKeyId,
661
-			$key,
662
-			Encryption::ID);
663
-	}
664
-
665
-	/**
666
-	 * add system keys such as the public share key and the recovery key
667
-	 *
668
-	 * @param array $accessList
669
-	 * @param array $publicKeys
670
-	 * @param string $uid
671
-	 * @return array
672
-	 * @throws PublicKeyMissingException
673
-	 */
674
-	public function addSystemKeys(array $accessList, array $publicKeys, $uid) {
675
-		if (!empty($accessList['public'])) {
676
-			$publicShareKey = $this->getPublicShareKey();
677
-			if (empty($publicShareKey)) {
678
-				throw new PublicKeyMissingException($this->getPublicShareKeyId());
679
-			}
680
-			$publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
681
-		}
682
-
683
-		if ($this->recoveryKeyExists() &&
684
-			$this->util->isRecoveryEnabledForUser($uid)) {
685
-
686
-			$publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
687
-		}
688
-
689
-		return $publicKeys;
690
-	}
691
-
692
-	/**
693
-	 * get master key password
694
-	 *
695
-	 * @return string
696
-	 * @throws \Exception
697
-	 */
698
-	public function getMasterKeyPassword() {
699
-		$password = $this->config->getSystemValue('secret');
700
-		if (empty($password)){
701
-			throw new \Exception('Can not get secret from Nextcloud instance');
702
-		}
703
-
704
-		return $password;
705
-	}
706
-
707
-	/**
708
-	 * return master key id
709
-	 *
710
-	 * @return string
711
-	 */
712
-	public function getMasterKeyId() {
713
-		return $this->masterKeyId;
714
-	}
715
-
716
-	/**
717
-	 * get public master key
718
-	 *
719
-	 * @return string
720
-	 */
721
-	public function getPublicMasterKey() {
722
-		return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
723
-	}
43
+    /**
44
+     * @var Session
45
+     */
46
+    protected $session;
47
+    /**
48
+     * @var IStorage
49
+     */
50
+    private $keyStorage;
51
+    /**
52
+     * @var Crypt
53
+     */
54
+    private $crypt;
55
+    /**
56
+     * @var string
57
+     */
58
+    private $recoveryKeyId;
59
+    /**
60
+     * @var string
61
+     */
62
+    private $publicShareKeyId;
63
+    /**
64
+     * @var string
65
+     */
66
+    private $masterKeyId;
67
+    /**
68
+     * @var string UserID
69
+     */
70
+    private $keyId;
71
+    /**
72
+     * @var string
73
+     */
74
+    private $publicKeyId = 'publicKey';
75
+    /**
76
+     * @var string
77
+     */
78
+    private $privateKeyId = 'privateKey';
79
+
80
+    /**
81
+     * @var string
82
+     */
83
+    private $shareKeyId = 'shareKey';
84
+
85
+    /**
86
+     * @var string
87
+     */
88
+    private $fileKeyId = 'fileKey';
89
+    /**
90
+     * @var IConfig
91
+     */
92
+    private $config;
93
+    /**
94
+     * @var ILogger
95
+     */
96
+    private $log;
97
+    /**
98
+     * @var Util
99
+     */
100
+    private $util;
101
+
102
+    /**
103
+     * @param IStorage $keyStorage
104
+     * @param Crypt $crypt
105
+     * @param IConfig $config
106
+     * @param IUserSession $userSession
107
+     * @param Session $session
108
+     * @param ILogger $log
109
+     * @param Util $util
110
+     */
111
+    public function __construct(
112
+        IStorage $keyStorage,
113
+        Crypt $crypt,
114
+        IConfig $config,
115
+        IUserSession $userSession,
116
+        Session $session,
117
+        ILogger $log,
118
+        Util $util
119
+    ) {
120
+
121
+        $this->util = $util;
122
+        $this->session = $session;
123
+        $this->keyStorage = $keyStorage;
124
+        $this->crypt = $crypt;
125
+        $this->config = $config;
126
+        $this->log = $log;
127
+
128
+        $this->recoveryKeyId = $this->config->getAppValue('encryption',
129
+            'recoveryKeyId');
130
+        if (empty($this->recoveryKeyId)) {
131
+            $this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
132
+            $this->config->setAppValue('encryption',
133
+                'recoveryKeyId',
134
+                $this->recoveryKeyId);
135
+        }
136
+
137
+        $this->publicShareKeyId = $this->config->getAppValue('encryption',
138
+            'publicShareKeyId');
139
+        if (empty($this->publicShareKeyId)) {
140
+            $this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
141
+            $this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
142
+        }
143
+
144
+        $this->masterKeyId = $this->config->getAppValue('encryption',
145
+            'masterKeyId');
146
+        if (empty($this->masterKeyId)) {
147
+            $this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
148
+            $this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
149
+        }
150
+
151
+        $this->keyId = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
152
+        $this->log = $log;
153
+    }
154
+
155
+    /**
156
+     * check if key pair for public link shares exists, if not we create one
157
+     */
158
+    public function validateShareKey() {
159
+        $shareKey = $this->getPublicShareKey();
160
+        if (empty($shareKey)) {
161
+            $keyPair = $this->crypt->createKeyPair();
162
+
163
+            // Save public key
164
+            $this->keyStorage->setSystemUserKey(
165
+                $this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
166
+                Encryption::ID);
167
+
168
+            // Encrypt private key empty passphrase
169
+            $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
170
+            $header = $this->crypt->generateHeader();
171
+            $this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
172
+        }
173
+    }
174
+
175
+    /**
176
+     * check if a key pair for the master key exists, if not we create one
177
+     */
178
+    public function validateMasterKey() {
179
+
180
+        if ($this->util->isMasterKeyEnabled() === false) {
181
+            return;
182
+        }
183
+
184
+        $publicMasterKey = $this->getPublicMasterKey();
185
+        if (empty($publicMasterKey)) {
186
+            $keyPair = $this->crypt->createKeyPair();
187
+
188
+            // Save public key
189
+            $this->keyStorage->setSystemUserKey(
190
+                $this->masterKeyId . '.publicKey', $keyPair['publicKey'],
191
+                Encryption::ID);
192
+
193
+            // Encrypt private key with system password
194
+            $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
195
+            $header = $this->crypt->generateHeader();
196
+            $this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
197
+        }
198
+
199
+        if (!$this->session->isPrivateKeySet()) {
200
+            $masterKey = $this->getSystemPrivateKey($this->masterKeyId);
201
+            $decryptedMasterKey = $this->crypt->decryptPrivateKey($masterKey, $this->getMasterKeyPassword(), $this->masterKeyId);
202
+            $this->session->setPrivateKey($decryptedMasterKey);
203
+        }
204
+
205
+        // after the encryption key is available we are ready to go
206
+        $this->session->setStatus(Session::INIT_SUCCESSFUL);
207
+    }
208
+
209
+    /**
210
+     * @return bool
211
+     */
212
+    public function recoveryKeyExists() {
213
+        $key = $this->getRecoveryKey();
214
+        return !empty($key);
215
+    }
216
+
217
+    /**
218
+     * get recovery key
219
+     *
220
+     * @return string
221
+     */
222
+    public function getRecoveryKey() {
223
+        return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
224
+    }
225
+
226
+    /**
227
+     * get recovery key ID
228
+     *
229
+     * @return string
230
+     */
231
+    public function getRecoveryKeyId() {
232
+        return $this->recoveryKeyId;
233
+    }
234
+
235
+    /**
236
+     * @param string $password
237
+     * @return bool
238
+     */
239
+    public function checkRecoveryPassword($password) {
240
+        $recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
241
+        $decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
242
+
243
+        if ($decryptedRecoveryKey) {
244
+            return true;
245
+        }
246
+        return false;
247
+    }
248
+
249
+    /**
250
+     * @param string $uid
251
+     * @param string $password
252
+     * @param string $keyPair
253
+     * @return bool
254
+     */
255
+    public function storeKeyPair($uid, $password, $keyPair) {
256
+        // Save Public Key
257
+        $this->setPublicKey($uid, $keyPair['publicKey']);
258
+
259
+        $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password, $uid);
260
+
261
+        $header = $this->crypt->generateHeader();
262
+
263
+        if ($encryptedKey) {
264
+            $this->setPrivateKey($uid, $header . $encryptedKey);
265
+            return true;
266
+        }
267
+        return false;
268
+    }
269
+
270
+    /**
271
+     * @param string $password
272
+     * @param array $keyPair
273
+     * @return bool
274
+     */
275
+    public function setRecoveryKey($password, $keyPair) {
276
+        // Save Public Key
277
+        $this->keyStorage->setSystemUserKey($this->getRecoveryKeyId().
278
+            '.publicKey',
279
+            $keyPair['publicKey'],
280
+            Encryption::ID);
281
+
282
+        $encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $password);
283
+        $header = $this->crypt->generateHeader();
284
+
285
+        if ($encryptedKey) {
286
+            $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
287
+            return true;
288
+        }
289
+        return false;
290
+    }
291
+
292
+    /**
293
+     * @param $userId
294
+     * @param $key
295
+     * @return bool
296
+     */
297
+    public function setPublicKey($userId, $key) {
298
+        return $this->keyStorage->setUserKey($userId, $this->publicKeyId, $key, Encryption::ID);
299
+    }
300
+
301
+    /**
302
+     * @param $userId
303
+     * @param string $key
304
+     * @return bool
305
+     */
306
+    public function setPrivateKey($userId, $key) {
307
+        return $this->keyStorage->setUserKey($userId,
308
+            $this->privateKeyId,
309
+            $key,
310
+            Encryption::ID);
311
+    }
312
+
313
+    /**
314
+     * write file key to key storage
315
+     *
316
+     * @param string $path
317
+     * @param string $key
318
+     * @return boolean
319
+     */
320
+    public function setFileKey($path, $key) {
321
+        return $this->keyStorage->setFileKey($path, $this->fileKeyId, $key, Encryption::ID);
322
+    }
323
+
324
+    /**
325
+     * set all file keys (the file key and the corresponding share keys)
326
+     *
327
+     * @param string $path
328
+     * @param array $keys
329
+     */
330
+    public function setAllFileKeys($path, $keys) {
331
+        $this->setFileKey($path, $keys['data']);
332
+        foreach ($keys['keys'] as $uid => $keyFile) {
333
+            $this->setShareKey($path, $uid, $keyFile);
334
+        }
335
+    }
336
+
337
+    /**
338
+     * write share key to the key storage
339
+     *
340
+     * @param string $path
341
+     * @param string $uid
342
+     * @param string $key
343
+     * @return boolean
344
+     */
345
+    public function setShareKey($path, $uid, $key) {
346
+        $keyId = $uid . '.' . $this->shareKeyId;
347
+        return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
348
+    }
349
+
350
+    /**
351
+     * Decrypt private key and store it
352
+     *
353
+     * @param string $uid user id
354
+     * @param string $passPhrase users password
355
+     * @return boolean
356
+     */
357
+    public function init($uid, $passPhrase) {
358
+
359
+        $this->session->setStatus(Session::INIT_EXECUTED);
360
+
361
+        try {
362
+            if($this->util->isMasterKeyEnabled()) {
363
+                $uid = $this->getMasterKeyId();
364
+                $passPhrase = $this->getMasterKeyPassword();
365
+                $privateKey = $this->getSystemPrivateKey($uid);
366
+            } else {
367
+                $privateKey = $this->getPrivateKey($uid);
368
+            }
369
+            $privateKey = $this->crypt->decryptPrivateKey($privateKey, $passPhrase, $uid);
370
+        } catch (PrivateKeyMissingException $e) {
371
+            return false;
372
+        } catch (DecryptionFailedException $e) {
373
+            return false;
374
+        } catch (\Exception $e) {
375
+            $this->log->logException($e, [
376
+                'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.',
377
+                'level' => ILogger::WARN,
378
+                'app' => 'encryption',
379
+            ]);
380
+            return false;
381
+        }
382
+
383
+        if ($privateKey) {
384
+            $this->session->setPrivateKey($privateKey);
385
+            $this->session->setStatus(Session::INIT_SUCCESSFUL);
386
+            return true;
387
+        }
388
+
389
+        return false;
390
+    }
391
+
392
+    /**
393
+     * @param $userId
394
+     * @return string
395
+     * @throws PrivateKeyMissingException
396
+     */
397
+    public function getPrivateKey($userId) {
398
+        $privateKey = $this->keyStorage->getUserKey($userId,
399
+            $this->privateKeyId, Encryption::ID);
400
+
401
+        if (strlen($privateKey) !== 0) {
402
+            return $privateKey;
403
+        }
404
+        throw new PrivateKeyMissingException($userId);
405
+    }
406
+
407
+    /**
408
+     * @param string $path
409
+     * @param $uid
410
+     * @return string
411
+     */
412
+    public function getFileKey($path, $uid) {
413
+        if ($uid === '') {
414
+            $uid = null;
415
+        }
416
+        $publicAccess = is_null($uid);
417
+        $encryptedFileKey = $this->keyStorage->getFileKey($path, $this->fileKeyId, Encryption::ID);
418
+
419
+        if (empty($encryptedFileKey)) {
420
+            return '';
421
+        }
422
+
423
+        if ($this->util->isMasterKeyEnabled()) {
424
+            $uid = $this->getMasterKeyId();
425
+            $shareKey = $this->getShareKey($path, $uid);
426
+            if ($publicAccess) {
427
+                $privateKey = $this->getSystemPrivateKey($uid);
428
+                $privateKey = $this->crypt->decryptPrivateKey($privateKey, $this->getMasterKeyPassword(), $uid);
429
+            } else {
430
+                // when logged in, the master key is already decrypted in the session
431
+                $privateKey = $this->session->getPrivateKey();
432
+            }
433
+        } else if ($publicAccess) {
434
+            // use public share key for public links
435
+            $uid = $this->getPublicShareKeyId();
436
+            $shareKey = $this->getShareKey($path, $uid);
437
+            $privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
438
+            $privateKey = $this->crypt->decryptPrivateKey($privateKey);
439
+        } else {
440
+            $shareKey = $this->getShareKey($path, $uid);
441
+            $privateKey = $this->session->getPrivateKey();
442
+        }
443
+
444
+        if ($encryptedFileKey && $shareKey && $privateKey) {
445
+            return $this->crypt->multiKeyDecrypt($encryptedFileKey,
446
+                $shareKey,
447
+                $privateKey);
448
+        }
449
+
450
+        return '';
451
+    }
452
+
453
+    /**
454
+     * Get the current version of a file
455
+     *
456
+     * @param string $path
457
+     * @param View $view
458
+     * @return int
459
+     */
460
+    public function getVersion($path, View $view) {
461
+        $fileInfo = $view->getFileInfo($path);
462
+        if($fileInfo === false) {
463
+            return 0;
464
+        }
465
+        return $fileInfo->getEncryptedVersion();
466
+    }
467
+
468
+    /**
469
+     * Set the current version of a file
470
+     *
471
+     * @param string $path
472
+     * @param int $version
473
+     * @param View $view
474
+     */
475
+    public function setVersion($path, $version, View $view) {
476
+        $fileInfo= $view->getFileInfo($path);
477
+
478
+        if($fileInfo !== false) {
479
+            $cache = $fileInfo->getStorage()->getCache();
480
+            $cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]);
481
+        }
482
+    }
483
+
484
+    /**
485
+     * get the encrypted file key
486
+     *
487
+     * @param string $path
488
+     * @return string
489
+     */
490
+    public function getEncryptedFileKey($path) {
491
+        $encryptedFileKey = $this->keyStorage->getFileKey($path,
492
+            $this->fileKeyId, Encryption::ID);
493
+
494
+        return $encryptedFileKey;
495
+    }
496
+
497
+    /**
498
+     * delete share key
499
+     *
500
+     * @param string $path
501
+     * @param string $keyId
502
+     * @return boolean
503
+     */
504
+    public function deleteShareKey($path, $keyId) {
505
+        return $this->keyStorage->deleteFileKey(
506
+            $path,
507
+            $keyId . '.' . $this->shareKeyId,
508
+            Encryption::ID);
509
+    }
510
+
511
+
512
+    /**
513
+     * @param $path
514
+     * @param $uid
515
+     * @return mixed
516
+     */
517
+    public function getShareKey($path, $uid) {
518
+        $keyId = $uid . '.' . $this->shareKeyId;
519
+        return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
520
+    }
521
+
522
+    /**
523
+     * check if user has a private and a public key
524
+     *
525
+     * @param string $userId
526
+     * @return bool
527
+     * @throws PrivateKeyMissingException
528
+     * @throws PublicKeyMissingException
529
+     */
530
+    public function userHasKeys($userId) {
531
+        $privateKey = $publicKey = true;
532
+        $exception = null;
533
+
534
+        try {
535
+            $this->getPrivateKey($userId);
536
+        } catch (PrivateKeyMissingException $e) {
537
+            $privateKey = false;
538
+            $exception = $e;
539
+        }
540
+        try {
541
+            $this->getPublicKey($userId);
542
+        } catch (PublicKeyMissingException $e) {
543
+            $publicKey = false;
544
+            $exception = $e;
545
+        }
546
+
547
+        if ($privateKey && $publicKey) {
548
+            return true;
549
+        } elseif (!$privateKey && !$publicKey) {
550
+            return false;
551
+        } else {
552
+            throw $exception;
553
+        }
554
+    }
555
+
556
+    /**
557
+     * @param $userId
558
+     * @return mixed
559
+     * @throws PublicKeyMissingException
560
+     */
561
+    public function getPublicKey($userId) {
562
+        $publicKey = $this->keyStorage->getUserKey($userId, $this->publicKeyId, Encryption::ID);
563
+
564
+        if (strlen($publicKey) !== 0) {
565
+            return $publicKey;
566
+        }
567
+        throw new PublicKeyMissingException($userId);
568
+    }
569
+
570
+    public function getPublicShareKeyId() {
571
+        return $this->publicShareKeyId;
572
+    }
573
+
574
+    /**
575
+     * get public key for public link shares
576
+     *
577
+     * @return string
578
+     */
579
+    public function getPublicShareKey() {
580
+        return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
581
+    }
582
+
583
+    /**
584
+     * @param string $purpose
585
+     * @param string $uid
586
+     */
587
+    public function backupUserKeys($purpose, $uid) {
588
+        $this->keyStorage->backupUserKeys(Encryption::ID, $purpose, $uid);
589
+    }
590
+
591
+    /**
592
+     * creat a backup of the users private and public key and then  delete it
593
+     *
594
+     * @param string $uid
595
+     */
596
+    public function deleteUserKeys($uid) {
597
+        $this->deletePublicKey($uid);
598
+        $this->deletePrivateKey($uid);
599
+    }
600
+
601
+    /**
602
+     * @param $uid
603
+     * @return bool
604
+     */
605
+    public function deletePublicKey($uid) {
606
+        return $this->keyStorage->deleteUserKey($uid, $this->publicKeyId, Encryption::ID);
607
+    }
608
+
609
+    /**
610
+     * @param string $uid
611
+     * @return bool
612
+     */
613
+    private function deletePrivateKey($uid) {
614
+        return $this->keyStorage->deleteUserKey($uid, $this->privateKeyId, Encryption::ID);
615
+    }
616
+
617
+    /**
618
+     * @param string $path
619
+     * @return bool
620
+     */
621
+    public function deleteAllFileKeys($path) {
622
+        return $this->keyStorage->deleteAllFileKeys($path);
623
+    }
624
+
625
+    /**
626
+     * @param array $userIds
627
+     * @return array
628
+     * @throws PublicKeyMissingException
629
+     */
630
+    public function getPublicKeys(array $userIds) {
631
+        $keys = [];
632
+
633
+        foreach ($userIds as $userId) {
634
+            try {
635
+                $keys[$userId] = $this->getPublicKey($userId);
636
+            } catch (PublicKeyMissingException $e) {
637
+                continue;
638
+            }
639
+        }
640
+
641
+        return $keys;
642
+
643
+    }
644
+
645
+    /**
646
+     * @param string $keyId
647
+     * @return string returns openssl key
648
+     */
649
+    public function getSystemPrivateKey($keyId) {
650
+        return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
651
+    }
652
+
653
+    /**
654
+     * @param string $keyId
655
+     * @param string $key
656
+     * @return string returns openssl key
657
+     */
658
+    public function setSystemPrivateKey($keyId, $key) {
659
+        return $this->keyStorage->setSystemUserKey(
660
+            $keyId . '.' . $this->privateKeyId,
661
+            $key,
662
+            Encryption::ID);
663
+    }
664
+
665
+    /**
666
+     * add system keys such as the public share key and the recovery key
667
+     *
668
+     * @param array $accessList
669
+     * @param array $publicKeys
670
+     * @param string $uid
671
+     * @return array
672
+     * @throws PublicKeyMissingException
673
+     */
674
+    public function addSystemKeys(array $accessList, array $publicKeys, $uid) {
675
+        if (!empty($accessList['public'])) {
676
+            $publicShareKey = $this->getPublicShareKey();
677
+            if (empty($publicShareKey)) {
678
+                throw new PublicKeyMissingException($this->getPublicShareKeyId());
679
+            }
680
+            $publicKeys[$this->getPublicShareKeyId()] = $publicShareKey;
681
+        }
682
+
683
+        if ($this->recoveryKeyExists() &&
684
+            $this->util->isRecoveryEnabledForUser($uid)) {
685
+
686
+            $publicKeys[$this->getRecoveryKeyId()] = $this->getRecoveryKey();
687
+        }
688
+
689
+        return $publicKeys;
690
+    }
691
+
692
+    /**
693
+     * get master key password
694
+     *
695
+     * @return string
696
+     * @throws \Exception
697
+     */
698
+    public function getMasterKeyPassword() {
699
+        $password = $this->config->getSystemValue('secret');
700
+        if (empty($password)){
701
+            throw new \Exception('Can not get secret from Nextcloud instance');
702
+        }
703
+
704
+        return $password;
705
+    }
706
+
707
+    /**
708
+     * return master key id
709
+     *
710
+     * @return string
711
+     */
712
+    public function getMasterKeyId() {
713
+        return $this->masterKeyId;
714
+    }
715
+
716
+    /**
717
+     * get public master key
718
+     *
719
+     * @return string
720
+     */
721
+    public function getPublicMasterKey() {
722
+        return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
723
+    }
724 724
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -128,7 +128,7 @@  discard block
 block discarded – undo
128 128
 		$this->recoveryKeyId = $this->config->getAppValue('encryption',
129 129
 			'recoveryKeyId');
130 130
 		if (empty($this->recoveryKeyId)) {
131
-			$this->recoveryKeyId = 'recoveryKey_' . substr(md5(time()), 0, 8);
131
+			$this->recoveryKeyId = 'recoveryKey_'.substr(md5(time()), 0, 8);
132 132
 			$this->config->setAppValue('encryption',
133 133
 				'recoveryKeyId',
134 134
 				$this->recoveryKeyId);
@@ -137,14 +137,14 @@  discard block
 block discarded – undo
137 137
 		$this->publicShareKeyId = $this->config->getAppValue('encryption',
138 138
 			'publicShareKeyId');
139 139
 		if (empty($this->publicShareKeyId)) {
140
-			$this->publicShareKeyId = 'pubShare_' . substr(md5(time()), 0, 8);
140
+			$this->publicShareKeyId = 'pubShare_'.substr(md5(time()), 0, 8);
141 141
 			$this->config->setAppValue('encryption', 'publicShareKeyId', $this->publicShareKeyId);
142 142
 		}
143 143
 
144 144
 		$this->masterKeyId = $this->config->getAppValue('encryption',
145 145
 			'masterKeyId');
146 146
 		if (empty($this->masterKeyId)) {
147
-			$this->masterKeyId = 'master_' . substr(md5(time()), 0, 8);
147
+			$this->masterKeyId = 'master_'.substr(md5(time()), 0, 8);
148 148
 			$this->config->setAppValue('encryption', 'masterKeyId', $this->masterKeyId);
149 149
 		}
150 150
 
@@ -162,13 +162,13 @@  discard block
 block discarded – undo
162 162
 
163 163
 			// Save public key
164 164
 			$this->keyStorage->setSystemUserKey(
165
-				$this->publicShareKeyId . '.publicKey', $keyPair['publicKey'],
165
+				$this->publicShareKeyId.'.publicKey', $keyPair['publicKey'],
166 166
 				Encryption::ID);
167 167
 
168 168
 			// Encrypt private key empty passphrase
169 169
 			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], '');
170 170
 			$header = $this->crypt->generateHeader();
171
-			$this->setSystemPrivateKey($this->publicShareKeyId, $header . $encryptedKey);
171
+			$this->setSystemPrivateKey($this->publicShareKeyId, $header.$encryptedKey);
172 172
 		}
173 173
 	}
174 174
 
@@ -187,13 +187,13 @@  discard block
 block discarded – undo
187 187
 
188 188
 			// Save public key
189 189
 			$this->keyStorage->setSystemUserKey(
190
-				$this->masterKeyId . '.publicKey', $keyPair['publicKey'],
190
+				$this->masterKeyId.'.publicKey', $keyPair['publicKey'],
191 191
 				Encryption::ID);
192 192
 
193 193
 			// Encrypt private key with system password
194 194
 			$encryptedKey = $this->crypt->encryptPrivateKey($keyPair['privateKey'], $this->getMasterKeyPassword(), $this->masterKeyId);
195 195
 			$header = $this->crypt->generateHeader();
196
-			$this->setSystemPrivateKey($this->masterKeyId, $header . $encryptedKey);
196
+			$this->setSystemPrivateKey($this->masterKeyId, $header.$encryptedKey);
197 197
 		}
198 198
 
199 199
 		if (!$this->session->isPrivateKeySet()) {
@@ -220,7 +220,7 @@  discard block
 block discarded – undo
220 220
 	 * @return string
221 221
 	 */
222 222
 	public function getRecoveryKey() {
223
-		return $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.publicKey', Encryption::ID);
223
+		return $this->keyStorage->getSystemUserKey($this->recoveryKeyId.'.publicKey', Encryption::ID);
224 224
 	}
225 225
 
226 226
 	/**
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
 	 * @return bool
238 238
 	 */
239 239
 	public function checkRecoveryPassword($password) {
240
-		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId . '.privateKey', Encryption::ID);
240
+		$recoveryKey = $this->keyStorage->getSystemUserKey($this->recoveryKeyId.'.privateKey', Encryption::ID);
241 241
 		$decryptedRecoveryKey = $this->crypt->decryptPrivateKey($recoveryKey, $password);
242 242
 
243 243
 		if ($decryptedRecoveryKey) {
@@ -261,7 +261,7 @@  discard block
 block discarded – undo
261 261
 		$header = $this->crypt->generateHeader();
262 262
 
263 263
 		if ($encryptedKey) {
264
-			$this->setPrivateKey($uid, $header . $encryptedKey);
264
+			$this->setPrivateKey($uid, $header.$encryptedKey);
265 265
 			return true;
266 266
 		}
267 267
 		return false;
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
 		$header = $this->crypt->generateHeader();
284 284
 
285 285
 		if ($encryptedKey) {
286
-			$this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
286
+			$this->setSystemPrivateKey($this->getRecoveryKeyId(), $header.$encryptedKey);
287 287
 			return true;
288 288
 		}
289 289
 		return false;
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 	 * @return boolean
344 344
 	 */
345 345
 	public function setShareKey($path, $uid, $key) {
346
-		$keyId = $uid . '.' . $this->shareKeyId;
346
+		$keyId = $uid.'.'.$this->shareKeyId;
347 347
 		return $this->keyStorage->setFileKey($path, $keyId, $key, Encryption::ID);
348 348
 	}
349 349
 
@@ -359,7 +359,7 @@  discard block
 block discarded – undo
359 359
 		$this->session->setStatus(Session::INIT_EXECUTED);
360 360
 
361 361
 		try {
362
-			if($this->util->isMasterKeyEnabled()) {
362
+			if ($this->util->isMasterKeyEnabled()) {
363 363
 				$uid = $this->getMasterKeyId();
364 364
 				$passPhrase = $this->getMasterKeyPassword();
365 365
 				$privateKey = $this->getSystemPrivateKey($uid);
@@ -373,7 +373,7 @@  discard block
 block discarded – undo
373 373
 			return false;
374 374
 		} catch (\Exception $e) {
375 375
 			$this->log->logException($e, [
376
-				'message' => 'Could not decrypt the private key from user "' . $uid . '"" during login. Assume password change on the user back-end.',
376
+				'message' => 'Could not decrypt the private key from user "'.$uid.'"" during login. Assume password change on the user back-end.',
377 377
 				'level' => ILogger::WARN,
378 378
 				'app' => 'encryption',
379 379
 			]);
@@ -434,7 +434,7 @@  discard block
 block discarded – undo
434 434
 			// use public share key for public links
435 435
 			$uid = $this->getPublicShareKeyId();
436 436
 			$shareKey = $this->getShareKey($path, $uid);
437
-			$privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.privateKey', Encryption::ID);
437
+			$privateKey = $this->keyStorage->getSystemUserKey($this->publicShareKeyId.'.privateKey', Encryption::ID);
438 438
 			$privateKey = $this->crypt->decryptPrivateKey($privateKey);
439 439
 		} else {
440 440
 			$shareKey = $this->getShareKey($path, $uid);
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
 	 */
460 460
 	public function getVersion($path, View $view) {
461 461
 		$fileInfo = $view->getFileInfo($path);
462
-		if($fileInfo === false) {
462
+		if ($fileInfo === false) {
463 463
 			return 0;
464 464
 		}
465 465
 		return $fileInfo->getEncryptedVersion();
@@ -473,9 +473,9 @@  discard block
 block discarded – undo
473 473
 	 * @param View $view
474 474
 	 */
475 475
 	public function setVersion($path, $version, View $view) {
476
-		$fileInfo= $view->getFileInfo($path);
476
+		$fileInfo = $view->getFileInfo($path);
477 477
 
478
-		if($fileInfo !== false) {
478
+		if ($fileInfo !== false) {
479 479
 			$cache = $fileInfo->getStorage()->getCache();
480 480
 			$cache->update($fileInfo->getId(), ['encrypted' => $version, 'encryptedVersion' => $version]);
481 481
 		}
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
 	public function deleteShareKey($path, $keyId) {
505 505
 		return $this->keyStorage->deleteFileKey(
506 506
 			$path,
507
-			$keyId . '.' . $this->shareKeyId,
507
+			$keyId.'.'.$this->shareKeyId,
508 508
 			Encryption::ID);
509 509
 	}
510 510
 
@@ -515,7 +515,7 @@  discard block
 block discarded – undo
515 515
 	 * @return mixed
516 516
 	 */
517 517
 	public function getShareKey($path, $uid) {
518
-		$keyId = $uid . '.' . $this->shareKeyId;
518
+		$keyId = $uid.'.'.$this->shareKeyId;
519 519
 		return $this->keyStorage->getFileKey($path, $keyId, Encryption::ID);
520 520
 	}
521 521
 
@@ -577,7 +577,7 @@  discard block
 block discarded – undo
577 577
 	 * @return string
578 578
 	 */
579 579
 	public function getPublicShareKey() {
580
-		return $this->keyStorage->getSystemUserKey($this->publicShareKeyId . '.publicKey', Encryption::ID);
580
+		return $this->keyStorage->getSystemUserKey($this->publicShareKeyId.'.publicKey', Encryption::ID);
581 581
 	}
582 582
 
583 583
 	/**
@@ -647,7 +647,7 @@  discard block
 block discarded – undo
647 647
 	 * @return string returns openssl key
648 648
 	 */
649 649
 	public function getSystemPrivateKey($keyId) {
650
-		return $this->keyStorage->getSystemUserKey($keyId . '.' . $this->privateKeyId, Encryption::ID);
650
+		return $this->keyStorage->getSystemUserKey($keyId.'.'.$this->privateKeyId, Encryption::ID);
651 651
 	}
652 652
 
653 653
 	/**
@@ -657,7 +657,7 @@  discard block
 block discarded – undo
657 657
 	 */
658 658
 	public function setSystemPrivateKey($keyId, $key) {
659 659
 		return $this->keyStorage->setSystemUserKey(
660
-			$keyId . '.' . $this->privateKeyId,
660
+			$keyId.'.'.$this->privateKeyId,
661 661
 			$key,
662 662
 			Encryption::ID);
663 663
 	}
@@ -697,7 +697,7 @@  discard block
 block discarded – undo
697 697
 	 */
698 698
 	public function getMasterKeyPassword() {
699 699
 		$password = $this->config->getSystemValue('secret');
700
-		if (empty($password)){
700
+		if (empty($password)) {
701 701
 			throw new \Exception('Can not get secret from Nextcloud instance');
702 702
 		}
703 703
 
@@ -719,6 +719,6 @@  discard block
 block discarded – undo
719 719
 	 * @return string
720 720
 	 */
721 721
 	public function getPublicMasterKey() {
722
-		return $this->keyStorage->getSystemUserKey($this->masterKeyId . '.publicKey', Encryption::ID);
722
+		return $this->keyStorage->getSystemUserKey($this->masterKeyId.'.publicKey', Encryption::ID);
723 723
 	}
724 724
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/FederatedShareProvider.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
 	/**
392 392
 	 * store remote ID in federated reShare table
393 393
 	 *
394
-	 * @param $shareId
394
+	 * @param integer $shareId
395 395
 	 * @param $remoteId
396 396
 	 */
397 397
 	public function storeRemoteId($shareId, $remoteId) {
@@ -729,7 +729,7 @@  discard block
 block discarded – undo
729 729
 	/**
730 730
 	 * get database row of a give share
731 731
 	 *
732
-	 * @param $id
732
+	 * @param integer $id
733 733
 	 * @return array
734 734
 	 * @throws ShareNotFound
735 735
 	 */
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -192,7 +192,7 @@  discard block
 block discarded – undo
192 192
 		if ($remoteShare) {
193 193
 			try {
194 194
 				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
195
-				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
195
+				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_'.time());
196 196
 				$share->setId($shareId);
197 197
 				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
198 198
 				// remote share was create successfully if we get a valid token as return
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 			$failure = true;
273 273
 		}
274 274
 
275
-		if($failure) {
275
+		if ($failure) {
276 276
 			$this->removeShareFromTableById($shareId);
277 277
 			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
278 278
 				[$share->getNode()->getName(), $share->getSharedWith()]);
@@ -324,7 +324,7 @@  discard block
 block discarded – undo
324 324
 			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
325 325
 		$result = $query->execute()->fetchAll();
326 326
 
327
-		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
327
+		if (isset($result[0]) && (int) $result[0]['remote_id'] > 0) {
328 328
 			return $result[0];
329 329
 		}
330 330
 
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
 		$qb->execute();
367 367
 		$id = $qb->getLastInsertId();
368 368
 
369
-		return (int)$id;
369
+		return (int) $id;
370 370
 	}
371 371
 
372 372
 	/**
@@ -456,14 +456,14 @@  discard block
 block discarded – undo
456 456
 	public function getRemoteId(IShare $share) {
457 457
 		$query = $this->dbConnection->getQueryBuilder();
458 458
 		$query->select('remote_id')->from('federated_reshares')
459
-			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
459
+			->where($query->expr()->eq('share_id', $query->createNamedParameter((int) $share->getId())));
460 460
 		$data = $query->execute()->fetch();
461 461
 
462 462
 		if (!is_array($data) || !isset($data['remote_id'])) {
463 463
 			throw new ShareNotFound();
464 464
 		}
465 465
 
466
-		return (int)$data['remote_id'];
466
+		return (int) $data['remote_id'];
467 467
 	}
468 468
 
469 469
 	/**
@@ -494,7 +494,7 @@  discard block
 block discarded – undo
494 494
 			->orderBy('id');
495 495
 
496 496
 		$cursor = $qb->execute();
497
-		while($data = $cursor->fetch()) {
497
+		while ($data = $cursor->fetch()) {
498 498
 			$children[] = $this->createShareObject($data);
499 499
 		}
500 500
 		$cursor->closeCursor();
@@ -612,7 +612,7 @@  discard block
 block discarded – undo
612 612
 			);
613 613
 		}
614 614
 
615
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
615
+		$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
616 616
 		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
617 617
 
618 618
 		$qb->orderBy('id');
@@ -675,7 +675,7 @@  discard block
 block discarded – undo
675 675
 
676 676
 		$cursor = $qb->execute();
677 677
 		$shares = [];
678
-		while($data = $cursor->fetch()) {
678
+		while ($data = $cursor->fetch()) {
679 679
 			$shares[] = $this->createShareObject($data);
680 680
 		}
681 681
 		$cursor->closeCursor();
@@ -699,7 +699,7 @@  discard block
 block discarded – undo
699 699
 		$cursor->closeCursor();
700 700
 
701 701
 		if ($data === false) {
702
-			throw new ShareNotFound('Can not find share with ID: ' . $id);
702
+			throw new ShareNotFound('Can not find share with ID: '.$id);
703 703
 		}
704 704
 
705 705
 		try {
@@ -727,7 +727,7 @@  discard block
 block discarded – undo
727 727
 			->execute();
728 728
 
729 729
 		$shares = [];
730
-		while($data = $cursor->fetch()) {
730
+		while ($data = $cursor->fetch()) {
731 731
 			$shares[] = $this->createShareObject($data);
732 732
 		}
733 733
 		$cursor->closeCursor();
@@ -766,7 +766,7 @@  discard block
 block discarded – undo
766 766
 
767 767
 		$cursor = $qb->execute();
768 768
 
769
-		while($data = $cursor->fetch()) {
769
+		while ($data = $cursor->fetch()) {
770 770
 			$shares[] = $this->createShareObject($data);
771 771
 		}
772 772
 		$cursor->closeCursor();
@@ -843,15 +843,15 @@  discard block
 block discarded – undo
843 843
 	private function createShareObject($data) {
844 844
 
845 845
 		$share = new Share($this->rootFolder, $this->userManager);
846
-		$share->setId((int)$data['id'])
847
-			->setShareType((int)$data['share_type'])
848
-			->setPermissions((int)$data['permissions'])
846
+		$share->setId((int) $data['id'])
847
+			->setShareType((int) $data['share_type'])
848
+			->setPermissions((int) $data['permissions'])
849 849
 			->setTarget($data['file_target'])
850
-			->setMailSend((bool)$data['mail_send'])
850
+			->setMailSend((bool) $data['mail_send'])
851 851
 			->setToken($data['token']);
852 852
 
853 853
 		$shareTime = new \DateTime();
854
-		$shareTime->setTimestamp((int)$data['stime']);
854
+		$shareTime->setTimestamp((int) $data['stime']);
855 855
 		$share->setShareTime($shareTime);
856 856
 		$share->setSharedWith($data['share_with']);
857 857
 
@@ -861,13 +861,13 @@  discard block
 block discarded – undo
861 861
 		} else {
862 862
 			//OLD SHARE
863 863
 			$share->setSharedBy($data['uid_owner']);
864
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
864
+			$path = $this->getNode($share->getSharedBy(), (int) $data['file_source']);
865 865
 
866 866
 			$owner = $path->getOwner();
867 867
 			$share->setShareOwner($owner->getUID());
868 868
 		}
869 869
 
870
-		$share->setNodeId((int)$data['file_source']);
870
+		$share->setNodeId((int) $data['file_source']);
871 871
 		$share->setNodeType($data['item_type']);
872 872
 
873 873
 		$share->setProviderId($this->identifier());
Please login to merge, or discard this patch.
Indentation   +966 added lines, -966 removed lines patch added patch discarded remove patch
@@ -53,980 +53,980 @@
 block discarded – undo
53 53
  */
54 54
 class FederatedShareProvider implements IShareProvider {
55 55
 
56
-	const SHARE_TYPE_REMOTE = 6;
57
-
58
-	/** @var IDBConnection */
59
-	private $dbConnection;
60
-
61
-	/** @var AddressHandler */
62
-	private $addressHandler;
63
-
64
-	/** @var Notifications */
65
-	private $notifications;
66
-
67
-	/** @var TokenHandler */
68
-	private $tokenHandler;
69
-
70
-	/** @var IL10N */
71
-	private $l;
72
-
73
-	/** @var ILogger */
74
-	private $logger;
75
-
76
-	/** @var IRootFolder */
77
-	private $rootFolder;
78
-
79
-	/** @var IConfig */
80
-	private $config;
81
-
82
-	/** @var string */
83
-	private $externalShareTable = 'share_external';
84
-
85
-	/** @var IUserManager */
86
-	private $userManager;
87
-
88
-	/** @var ICloudIdManager */
89
-	private $cloudIdManager;
90
-
91
-	/** @var \OCP\GlobalScale\IConfig */
92
-	private $gsConfig;
93
-
94
-	/**
95
-	 * DefaultShareProvider constructor.
96
-	 *
97
-	 * @param IDBConnection $connection
98
-	 * @param AddressHandler $addressHandler
99
-	 * @param Notifications $notifications
100
-	 * @param TokenHandler $tokenHandler
101
-	 * @param IL10N $l10n
102
-	 * @param ILogger $logger
103
-	 * @param IRootFolder $rootFolder
104
-	 * @param IConfig $config
105
-	 * @param IUserManager $userManager
106
-	 * @param ICloudIdManager $cloudIdManager
107
-	 * @param \OCP\GlobalScale\IConfig $globalScaleConfig
108
-	 */
109
-	public function __construct(
110
-			IDBConnection $connection,
111
-			AddressHandler $addressHandler,
112
-			Notifications $notifications,
113
-			TokenHandler $tokenHandler,
114
-			IL10N $l10n,
115
-			ILogger $logger,
116
-			IRootFolder $rootFolder,
117
-			IConfig $config,
118
-			IUserManager $userManager,
119
-			ICloudIdManager $cloudIdManager,
120
-			\OCP\GlobalScale\IConfig $globalScaleConfig
121
-	) {
122
-		$this->dbConnection = $connection;
123
-		$this->addressHandler = $addressHandler;
124
-		$this->notifications = $notifications;
125
-		$this->tokenHandler = $tokenHandler;
126
-		$this->l = $l10n;
127
-		$this->logger = $logger;
128
-		$this->rootFolder = $rootFolder;
129
-		$this->config = $config;
130
-		$this->userManager = $userManager;
131
-		$this->cloudIdManager = $cloudIdManager;
132
-		$this->gsConfig = $globalScaleConfig;
133
-	}
134
-
135
-	/**
136
-	 * Return the identifier of this provider.
137
-	 *
138
-	 * @return string Containing only [a-zA-Z0-9]
139
-	 */
140
-	public function identifier() {
141
-		return 'ocFederatedSharing';
142
-	}
143
-
144
-	/**
145
-	 * Share a path
146
-	 *
147
-	 * @param IShare $share
148
-	 * @return IShare The share object
149
-	 * @throws ShareNotFound
150
-	 * @throws \Exception
151
-	 */
152
-	public function create(IShare $share) {
153
-
154
-		$shareWith = $share->getSharedWith();
155
-		$itemSource = $share->getNodeId();
156
-		$itemType = $share->getNodeType();
157
-		$permissions = $share->getPermissions();
158
-		$sharedBy = $share->getSharedBy();
159
-
160
-		/*
56
+    const SHARE_TYPE_REMOTE = 6;
57
+
58
+    /** @var IDBConnection */
59
+    private $dbConnection;
60
+
61
+    /** @var AddressHandler */
62
+    private $addressHandler;
63
+
64
+    /** @var Notifications */
65
+    private $notifications;
66
+
67
+    /** @var TokenHandler */
68
+    private $tokenHandler;
69
+
70
+    /** @var IL10N */
71
+    private $l;
72
+
73
+    /** @var ILogger */
74
+    private $logger;
75
+
76
+    /** @var IRootFolder */
77
+    private $rootFolder;
78
+
79
+    /** @var IConfig */
80
+    private $config;
81
+
82
+    /** @var string */
83
+    private $externalShareTable = 'share_external';
84
+
85
+    /** @var IUserManager */
86
+    private $userManager;
87
+
88
+    /** @var ICloudIdManager */
89
+    private $cloudIdManager;
90
+
91
+    /** @var \OCP\GlobalScale\IConfig */
92
+    private $gsConfig;
93
+
94
+    /**
95
+     * DefaultShareProvider constructor.
96
+     *
97
+     * @param IDBConnection $connection
98
+     * @param AddressHandler $addressHandler
99
+     * @param Notifications $notifications
100
+     * @param TokenHandler $tokenHandler
101
+     * @param IL10N $l10n
102
+     * @param ILogger $logger
103
+     * @param IRootFolder $rootFolder
104
+     * @param IConfig $config
105
+     * @param IUserManager $userManager
106
+     * @param ICloudIdManager $cloudIdManager
107
+     * @param \OCP\GlobalScale\IConfig $globalScaleConfig
108
+     */
109
+    public function __construct(
110
+            IDBConnection $connection,
111
+            AddressHandler $addressHandler,
112
+            Notifications $notifications,
113
+            TokenHandler $tokenHandler,
114
+            IL10N $l10n,
115
+            ILogger $logger,
116
+            IRootFolder $rootFolder,
117
+            IConfig $config,
118
+            IUserManager $userManager,
119
+            ICloudIdManager $cloudIdManager,
120
+            \OCP\GlobalScale\IConfig $globalScaleConfig
121
+    ) {
122
+        $this->dbConnection = $connection;
123
+        $this->addressHandler = $addressHandler;
124
+        $this->notifications = $notifications;
125
+        $this->tokenHandler = $tokenHandler;
126
+        $this->l = $l10n;
127
+        $this->logger = $logger;
128
+        $this->rootFolder = $rootFolder;
129
+        $this->config = $config;
130
+        $this->userManager = $userManager;
131
+        $this->cloudIdManager = $cloudIdManager;
132
+        $this->gsConfig = $globalScaleConfig;
133
+    }
134
+
135
+    /**
136
+     * Return the identifier of this provider.
137
+     *
138
+     * @return string Containing only [a-zA-Z0-9]
139
+     */
140
+    public function identifier() {
141
+        return 'ocFederatedSharing';
142
+    }
143
+
144
+    /**
145
+     * Share a path
146
+     *
147
+     * @param IShare $share
148
+     * @return IShare The share object
149
+     * @throws ShareNotFound
150
+     * @throws \Exception
151
+     */
152
+    public function create(IShare $share) {
153
+
154
+        $shareWith = $share->getSharedWith();
155
+        $itemSource = $share->getNodeId();
156
+        $itemType = $share->getNodeType();
157
+        $permissions = $share->getPermissions();
158
+        $sharedBy = $share->getSharedBy();
159
+
160
+        /*
161 161
 		 * Check if file is not already shared with the remote user
162 162
 		 */
163
-		$alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
164
-		if (!empty($alreadyShared)) {
165
-			$message = 'Sharing %s failed, because this item is already shared with %s';
166
-			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
167
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
168
-			throw new \Exception($message_t);
169
-		}
170
-
171
-
172
-		// don't allow federated shares if source and target server are the same
173
-		$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
174
-		$currentServer = $this->addressHandler->generateRemoteURL();
175
-		$currentUser = $sharedBy;
176
-		if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
177
-			$message = 'Not allowed to create a federated share with the same user.';
178
-			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
179
-			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
180
-			throw new \Exception($message_t);
181
-		}
182
-
183
-
184
-		$share->setSharedWith($cloudId->getId());
185
-
186
-		try {
187
-			$remoteShare = $this->getShareFromExternalShareTable($share);
188
-		} catch (ShareNotFound $e) {
189
-			$remoteShare = null;
190
-		}
191
-
192
-		if ($remoteShare) {
193
-			try {
194
-				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
195
-				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
196
-				$share->setId($shareId);
197
-				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
198
-				// remote share was create successfully if we get a valid token as return
199
-				$send = is_string($token) && $token !== '';
200
-			} catch (\Exception $e) {
201
-				// fall back to old re-share behavior if the remote server
202
-				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
203
-				$this->removeShareFromTable($share);
204
-				$shareId = $this->createFederatedShare($share);
205
-			}
206
-			if ($send) {
207
-				$this->updateSuccessfulReshare($shareId, $token);
208
-				$this->storeRemoteId($shareId, $remoteId);
209
-			} else {
210
-				$this->removeShareFromTable($share);
211
-				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
212
-				throw new \Exception($message_t);
213
-			}
214
-
215
-		} else {
216
-			$shareId = $this->createFederatedShare($share);
217
-		}
218
-
219
-		$data = $this->getRawShare($shareId);
220
-		return $this->createShareObject($data);
221
-	}
222
-
223
-	/**
224
-	 * create federated share and inform the recipient
225
-	 *
226
-	 * @param IShare $share
227
-	 * @return int
228
-	 * @throws ShareNotFound
229
-	 * @throws \Exception
230
-	 */
231
-	protected function createFederatedShare(IShare $share) {
232
-		$token = $this->tokenHandler->generateToken();
233
-		$shareId = $this->addShareToDB(
234
-			$share->getNodeId(),
235
-			$share->getNodeType(),
236
-			$share->getSharedWith(),
237
-			$share->getSharedBy(),
238
-			$share->getShareOwner(),
239
-			$share->getPermissions(),
240
-			$token
241
-		);
242
-
243
-		$failure = false;
244
-
245
-		try {
246
-			$sharedByFederatedId = $share->getSharedBy();
247
-			if ($this->userManager->userExists($sharedByFederatedId)) {
248
-				$cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
249
-				$sharedByFederatedId = $cloudId->getId();
250
-			}
251
-			$ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
252
-			$send = $this->notifications->sendRemoteShare(
253
-				$token,
254
-				$share->getSharedWith(),
255
-				$share->getNode()->getName(),
256
-				$shareId,
257
-				$share->getShareOwner(),
258
-				$ownerCloudId->getId(),
259
-				$share->getSharedBy(),
260
-				$sharedByFederatedId
261
-			);
262
-
263
-			if ($send === false) {
264
-				$failure = true;
265
-			}
266
-		} catch (\Exception $e) {
267
-			$this->logger->logException($e, [
268
-				'message' => 'Failed to notify remote server of federated share, removing share.',
269
-				'level' => ILogger::ERROR,
270
-				'app' => 'federatedfilesharing',
271
-			]);
272
-			$failure = true;
273
-		}
274
-
275
-		if($failure) {
276
-			$this->removeShareFromTableById($shareId);
277
-			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
278
-				[$share->getNode()->getName(), $share->getSharedWith()]);
279
-			throw new \Exception($message_t);
280
-		}
281
-
282
-		return $shareId;
283
-
284
-	}
285
-
286
-	/**
287
-	 * @param string $shareWith
288
-	 * @param IShare $share
289
-	 * @param string $shareId internal share Id
290
-	 * @return array
291
-	 * @throws \Exception
292
-	 */
293
-	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
294
-
295
-		$remoteShare = $this->getShareFromExternalShareTable($share);
296
-		$token = $remoteShare['share_token'];
297
-		$remoteId = $remoteShare['remote_id'];
298
-		$remote = $remoteShare['remote'];
299
-
300
-		list($token, $remoteId) = $this->notifications->requestReShare(
301
-			$token,
302
-			$remoteId,
303
-			$shareId,
304
-			$remote,
305
-			$shareWith,
306
-			$share->getPermissions(),
307
-			$share->getNode()->getName()
308
-		);
309
-
310
-		return [$token, $remoteId];
311
-	}
312
-
313
-	/**
314
-	 * get federated share from the share_external table but exclude mounted link shares
315
-	 *
316
-	 * @param IShare $share
317
-	 * @return array
318
-	 * @throws ShareNotFound
319
-	 */
320
-	protected function getShareFromExternalShareTable(IShare $share) {
321
-		$query = $this->dbConnection->getQueryBuilder();
322
-		$query->select('*')->from($this->externalShareTable)
323
-			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
324
-			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
325
-		$result = $query->execute()->fetchAll();
326
-
327
-		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
328
-			return $result[0];
329
-		}
330
-
331
-		throw new ShareNotFound('share not found in share_external table');
332
-	}
333
-
334
-	/**
335
-	 * add share to the database and return the ID
336
-	 *
337
-	 * @param int $itemSource
338
-	 * @param string $itemType
339
-	 * @param string $shareWith
340
-	 * @param string $sharedBy
341
-	 * @param string $uidOwner
342
-	 * @param int $permissions
343
-	 * @param string $token
344
-	 * @return int
345
-	 */
346
-	private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
347
-		$qb = $this->dbConnection->getQueryBuilder();
348
-		$qb->insert('share')
349
-			->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
350
-			->setValue('item_type', $qb->createNamedParameter($itemType))
351
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
352
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
353
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
354
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
355
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
356
-			->setValue('permissions', $qb->createNamedParameter($permissions))
357
-			->setValue('token', $qb->createNamedParameter($token))
358
-			->setValue('stime', $qb->createNamedParameter(time()));
359
-
360
-		/*
163
+        $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
164
+        if (!empty($alreadyShared)) {
165
+            $message = 'Sharing %s failed, because this item is already shared with %s';
166
+            $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
167
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
168
+            throw new \Exception($message_t);
169
+        }
170
+
171
+
172
+        // don't allow federated shares if source and target server are the same
173
+        $cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
174
+        $currentServer = $this->addressHandler->generateRemoteURL();
175
+        $currentUser = $sharedBy;
176
+        if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
177
+            $message = 'Not allowed to create a federated share with the same user.';
178
+            $message_t = $this->l->t('Not allowed to create a federated share with the same user');
179
+            $this->logger->debug($message, ['app' => 'Federated File Sharing']);
180
+            throw new \Exception($message_t);
181
+        }
182
+
183
+
184
+        $share->setSharedWith($cloudId->getId());
185
+
186
+        try {
187
+            $remoteShare = $this->getShareFromExternalShareTable($share);
188
+        } catch (ShareNotFound $e) {
189
+            $remoteShare = null;
190
+        }
191
+
192
+        if ($remoteShare) {
193
+            try {
194
+                $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
195
+                $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
196
+                $share->setId($shareId);
197
+                list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
198
+                // remote share was create successfully if we get a valid token as return
199
+                $send = is_string($token) && $token !== '';
200
+            } catch (\Exception $e) {
201
+                // fall back to old re-share behavior if the remote server
202
+                // doesn't support flat re-shares (was introduced with Nextcloud 9.1)
203
+                $this->removeShareFromTable($share);
204
+                $shareId = $this->createFederatedShare($share);
205
+            }
206
+            if ($send) {
207
+                $this->updateSuccessfulReshare($shareId, $token);
208
+                $this->storeRemoteId($shareId, $remoteId);
209
+            } else {
210
+                $this->removeShareFromTable($share);
211
+                $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
212
+                throw new \Exception($message_t);
213
+            }
214
+
215
+        } else {
216
+            $shareId = $this->createFederatedShare($share);
217
+        }
218
+
219
+        $data = $this->getRawShare($shareId);
220
+        return $this->createShareObject($data);
221
+    }
222
+
223
+    /**
224
+     * create federated share and inform the recipient
225
+     *
226
+     * @param IShare $share
227
+     * @return int
228
+     * @throws ShareNotFound
229
+     * @throws \Exception
230
+     */
231
+    protected function createFederatedShare(IShare $share) {
232
+        $token = $this->tokenHandler->generateToken();
233
+        $shareId = $this->addShareToDB(
234
+            $share->getNodeId(),
235
+            $share->getNodeType(),
236
+            $share->getSharedWith(),
237
+            $share->getSharedBy(),
238
+            $share->getShareOwner(),
239
+            $share->getPermissions(),
240
+            $token
241
+        );
242
+
243
+        $failure = false;
244
+
245
+        try {
246
+            $sharedByFederatedId = $share->getSharedBy();
247
+            if ($this->userManager->userExists($sharedByFederatedId)) {
248
+                $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
249
+                $sharedByFederatedId = $cloudId->getId();
250
+            }
251
+            $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
252
+            $send = $this->notifications->sendRemoteShare(
253
+                $token,
254
+                $share->getSharedWith(),
255
+                $share->getNode()->getName(),
256
+                $shareId,
257
+                $share->getShareOwner(),
258
+                $ownerCloudId->getId(),
259
+                $share->getSharedBy(),
260
+                $sharedByFederatedId
261
+            );
262
+
263
+            if ($send === false) {
264
+                $failure = true;
265
+            }
266
+        } catch (\Exception $e) {
267
+            $this->logger->logException($e, [
268
+                'message' => 'Failed to notify remote server of federated share, removing share.',
269
+                'level' => ILogger::ERROR,
270
+                'app' => 'federatedfilesharing',
271
+            ]);
272
+            $failure = true;
273
+        }
274
+
275
+        if($failure) {
276
+            $this->removeShareFromTableById($shareId);
277
+            $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
278
+                [$share->getNode()->getName(), $share->getSharedWith()]);
279
+            throw new \Exception($message_t);
280
+        }
281
+
282
+        return $shareId;
283
+
284
+    }
285
+
286
+    /**
287
+     * @param string $shareWith
288
+     * @param IShare $share
289
+     * @param string $shareId internal share Id
290
+     * @return array
291
+     * @throws \Exception
292
+     */
293
+    protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
294
+
295
+        $remoteShare = $this->getShareFromExternalShareTable($share);
296
+        $token = $remoteShare['share_token'];
297
+        $remoteId = $remoteShare['remote_id'];
298
+        $remote = $remoteShare['remote'];
299
+
300
+        list($token, $remoteId) = $this->notifications->requestReShare(
301
+            $token,
302
+            $remoteId,
303
+            $shareId,
304
+            $remote,
305
+            $shareWith,
306
+            $share->getPermissions(),
307
+            $share->getNode()->getName()
308
+        );
309
+
310
+        return [$token, $remoteId];
311
+    }
312
+
313
+    /**
314
+     * get federated share from the share_external table but exclude mounted link shares
315
+     *
316
+     * @param IShare $share
317
+     * @return array
318
+     * @throws ShareNotFound
319
+     */
320
+    protected function getShareFromExternalShareTable(IShare $share) {
321
+        $query = $this->dbConnection->getQueryBuilder();
322
+        $query->select('*')->from($this->externalShareTable)
323
+            ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
324
+            ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
325
+        $result = $query->execute()->fetchAll();
326
+
327
+        if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
328
+            return $result[0];
329
+        }
330
+
331
+        throw new ShareNotFound('share not found in share_external table');
332
+    }
333
+
334
+    /**
335
+     * add share to the database and return the ID
336
+     *
337
+     * @param int $itemSource
338
+     * @param string $itemType
339
+     * @param string $shareWith
340
+     * @param string $sharedBy
341
+     * @param string $uidOwner
342
+     * @param int $permissions
343
+     * @param string $token
344
+     * @return int
345
+     */
346
+    private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
347
+        $qb = $this->dbConnection->getQueryBuilder();
348
+        $qb->insert('share')
349
+            ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
350
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
351
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
352
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
353
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
354
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
355
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
356
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
357
+            ->setValue('token', $qb->createNamedParameter($token))
358
+            ->setValue('stime', $qb->createNamedParameter(time()));
359
+
360
+        /*
361 361
 		 * Added to fix https://github.com/owncloud/core/issues/22215
362 362
 		 * Can be removed once we get rid of ajax/share.php
363 363
 		 */
364
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
365
-
366
-		$qb->execute();
367
-		$id = $qb->getLastInsertId();
368
-
369
-		return (int)$id;
370
-	}
371
-
372
-	/**
373
-	 * Update a share
374
-	 *
375
-	 * @param IShare $share
376
-	 * @return IShare The share object
377
-	 */
378
-	public function update(IShare $share) {
379
-		/*
364
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
365
+
366
+        $qb->execute();
367
+        $id = $qb->getLastInsertId();
368
+
369
+        return (int)$id;
370
+    }
371
+
372
+    /**
373
+     * Update a share
374
+     *
375
+     * @param IShare $share
376
+     * @return IShare The share object
377
+     */
378
+    public function update(IShare $share) {
379
+        /*
380 380
 		 * We allow updating the permissions of federated shares
381 381
 		 */
382
-		$qb = $this->dbConnection->getQueryBuilder();
383
-			$qb->update('share')
384
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
385
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
386
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
387
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
388
-				->execute();
389
-
390
-		// send the updated permission to the owner/initiator, if they are not the same
391
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
392
-			$this->sendPermissionUpdate($share);
393
-		}
394
-
395
-		return $share;
396
-	}
397
-
398
-	/**
399
-	 * send the updated permission to the owner/initiator, if they are not the same
400
-	 *
401
-	 * @param IShare $share
402
-	 * @throws ShareNotFound
403
-	 * @throws \OC\HintException
404
-	 */
405
-	protected function sendPermissionUpdate(IShare $share) {
406
-		$remoteId = $this->getRemoteId($share);
407
-		// if the local user is the owner we send the permission change to the initiator
408
-		if ($this->userManager->userExists($share->getShareOwner())) {
409
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
410
-		} else { // ... if not we send the permission change to the owner
411
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
412
-		}
413
-		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
414
-	}
415
-
416
-
417
-	/**
418
-	 * update successful reShare with the correct token
419
-	 *
420
-	 * @param int $shareId
421
-	 * @param string $token
422
-	 */
423
-	protected function updateSuccessfulReShare($shareId, $token) {
424
-		$query = $this->dbConnection->getQueryBuilder();
425
-		$query->update('share')
426
-			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
427
-			->set('token', $query->createNamedParameter($token))
428
-			->execute();
429
-	}
430
-
431
-	/**
432
-	 * store remote ID in federated reShare table
433
-	 *
434
-	 * @param $shareId
435
-	 * @param $remoteId
436
-	 */
437
-	public function storeRemoteId($shareId, $remoteId) {
438
-		$query = $this->dbConnection->getQueryBuilder();
439
-		$query->insert('federated_reshares')
440
-			->values(
441
-				[
442
-					'share_id' =>  $query->createNamedParameter($shareId),
443
-					'remote_id' => $query->createNamedParameter($remoteId),
444
-				]
445
-			);
446
-		$query->execute();
447
-	}
448
-
449
-	/**
450
-	 * get share ID on remote server for federated re-shares
451
-	 *
452
-	 * @param IShare $share
453
-	 * @return int
454
-	 * @throws ShareNotFound
455
-	 */
456
-	public function getRemoteId(IShare $share) {
457
-		$query = $this->dbConnection->getQueryBuilder();
458
-		$query->select('remote_id')->from('federated_reshares')
459
-			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
460
-		$data = $query->execute()->fetch();
461
-
462
-		if (!is_array($data) || !isset($data['remote_id'])) {
463
-			throw new ShareNotFound();
464
-		}
465
-
466
-		return (int)$data['remote_id'];
467
-	}
468
-
469
-	/**
470
-	 * @inheritdoc
471
-	 */
472
-	public function move(IShare $share, $recipient) {
473
-		/*
382
+        $qb = $this->dbConnection->getQueryBuilder();
383
+            $qb->update('share')
384
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
385
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
386
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
387
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
388
+                ->execute();
389
+
390
+        // send the updated permission to the owner/initiator, if they are not the same
391
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
392
+            $this->sendPermissionUpdate($share);
393
+        }
394
+
395
+        return $share;
396
+    }
397
+
398
+    /**
399
+     * send the updated permission to the owner/initiator, if they are not the same
400
+     *
401
+     * @param IShare $share
402
+     * @throws ShareNotFound
403
+     * @throws \OC\HintException
404
+     */
405
+    protected function sendPermissionUpdate(IShare $share) {
406
+        $remoteId = $this->getRemoteId($share);
407
+        // if the local user is the owner we send the permission change to the initiator
408
+        if ($this->userManager->userExists($share->getShareOwner())) {
409
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
410
+        } else { // ... if not we send the permission change to the owner
411
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
412
+        }
413
+        $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
414
+    }
415
+
416
+
417
+    /**
418
+     * update successful reShare with the correct token
419
+     *
420
+     * @param int $shareId
421
+     * @param string $token
422
+     */
423
+    protected function updateSuccessfulReShare($shareId, $token) {
424
+        $query = $this->dbConnection->getQueryBuilder();
425
+        $query->update('share')
426
+            ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
427
+            ->set('token', $query->createNamedParameter($token))
428
+            ->execute();
429
+    }
430
+
431
+    /**
432
+     * store remote ID in federated reShare table
433
+     *
434
+     * @param $shareId
435
+     * @param $remoteId
436
+     */
437
+    public function storeRemoteId($shareId, $remoteId) {
438
+        $query = $this->dbConnection->getQueryBuilder();
439
+        $query->insert('federated_reshares')
440
+            ->values(
441
+                [
442
+                    'share_id' =>  $query->createNamedParameter($shareId),
443
+                    'remote_id' => $query->createNamedParameter($remoteId),
444
+                ]
445
+            );
446
+        $query->execute();
447
+    }
448
+
449
+    /**
450
+     * get share ID on remote server for federated re-shares
451
+     *
452
+     * @param IShare $share
453
+     * @return int
454
+     * @throws ShareNotFound
455
+     */
456
+    public function getRemoteId(IShare $share) {
457
+        $query = $this->dbConnection->getQueryBuilder();
458
+        $query->select('remote_id')->from('federated_reshares')
459
+            ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
460
+        $data = $query->execute()->fetch();
461
+
462
+        if (!is_array($data) || !isset($data['remote_id'])) {
463
+            throw new ShareNotFound();
464
+        }
465
+
466
+        return (int)$data['remote_id'];
467
+    }
468
+
469
+    /**
470
+     * @inheritdoc
471
+     */
472
+    public function move(IShare $share, $recipient) {
473
+        /*
474 474
 		 * This function does nothing yet as it is just for outgoing
475 475
 		 * federated shares.
476 476
 		 */
477
-		return $share;
478
-	}
479
-
480
-	/**
481
-	 * Get all children of this share
482
-	 *
483
-	 * @param IShare $parent
484
-	 * @return IShare[]
485
-	 */
486
-	public function getChildren(IShare $parent) {
487
-		$children = [];
488
-
489
-		$qb = $this->dbConnection->getQueryBuilder();
490
-		$qb->select('*')
491
-			->from('share')
492
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
493
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
494
-			->orderBy('id');
495
-
496
-		$cursor = $qb->execute();
497
-		while($data = $cursor->fetch()) {
498
-			$children[] = $this->createShareObject($data);
499
-		}
500
-		$cursor->closeCursor();
501
-
502
-		return $children;
503
-	}
504
-
505
-	/**
506
-	 * Delete a share (owner unShares the file)
507
-	 *
508
-	 * @param IShare $share
509
-	 * @throws ShareNotFound
510
-	 * @throws \OC\HintException
511
-	 */
512
-	public function delete(IShare $share) {
513
-
514
-		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
515
-
516
-		// if the local user is the owner we can send the unShare request directly...
517
-		if ($this->userManager->userExists($share->getShareOwner())) {
518
-			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
519
-			$this->revokeShare($share, true);
520
-		} else { // ... if not we need to correct ID for the unShare request
521
-			$remoteId = $this->getRemoteId($share);
522
-			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
523
-			$this->revokeShare($share, false);
524
-		}
525
-
526
-		// only remove the share when all messages are send to not lose information
527
-		// about the share to early
528
-		$this->removeShareFromTable($share);
529
-	}
530
-
531
-	/**
532
-	 * in case of a re-share we need to send the other use (initiator or owner)
533
-	 * a message that the file was unshared
534
-	 *
535
-	 * @param IShare $share
536
-	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
537
-	 * @throws ShareNotFound
538
-	 * @throws \OC\HintException
539
-	 */
540
-	protected function revokeShare($share, $isOwner) {
541
-		// also send a unShare request to the initiator, if this is a different user than the owner
542
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
543
-			if ($isOwner) {
544
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
545
-			} else {
546
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
547
-			}
548
-			$remoteId = $this->getRemoteId($share);
549
-			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
550
-		}
551
-	}
552
-
553
-	/**
554
-	 * remove share from table
555
-	 *
556
-	 * @param IShare $share
557
-	 */
558
-	public function removeShareFromTable(IShare $share) {
559
-		$this->removeShareFromTableById($share->getId());
560
-	}
561
-
562
-	/**
563
-	 * remove share from table
564
-	 *
565
-	 * @param string $shareId
566
-	 */
567
-	private function removeShareFromTableById($shareId) {
568
-		$qb = $this->dbConnection->getQueryBuilder();
569
-		$qb->delete('share')
570
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
571
-		$qb->execute();
572
-
573
-		$qb->delete('federated_reshares')
574
-			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
575
-		$qb->execute();
576
-	}
577
-
578
-	/**
579
-	 * @inheritdoc
580
-	 */
581
-	public function deleteFromSelf(IShare $share, $recipient) {
582
-		// nothing to do here. Technically deleteFromSelf in the context of federated
583
-		// shares is a umount of a external storage. This is handled here
584
-		// apps/files_sharing/lib/external/manager.php
585
-		// TODO move this code over to this app
586
-	}
587
-
588
-
589
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
590
-		$qb = $this->dbConnection->getQueryBuilder();
591
-		$qb->select('*')
592
-			->from('share', 's')
593
-			->andWhere($qb->expr()->orX(
594
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
595
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
596
-			))
597
-			->andWhere(
598
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
599
-			);
600
-
601
-		/**
602
-		 * Reshares for this user are shares where they are the owner.
603
-		 */
604
-		if ($reshares === false) {
605
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
606
-		} else {
607
-			$qb->andWhere(
608
-				$qb->expr()->orX(
609
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
610
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
611
-				)
612
-			);
613
-		}
614
-
615
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
616
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
617
-
618
-		$qb->orderBy('id');
619
-
620
-		$cursor = $qb->execute();
621
-		$shares = [];
622
-		while ($data = $cursor->fetch()) {
623
-			$shares[$data['fileid']][] = $this->createShareObject($data);
624
-		}
625
-		$cursor->closeCursor();
626
-
627
-		return $shares;
628
-	}
629
-
630
-	/**
631
-	 * @inheritdoc
632
-	 */
633
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
634
-		$qb = $this->dbConnection->getQueryBuilder();
635
-		$qb->select('*')
636
-			->from('share');
637
-
638
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
639
-
640
-		/**
641
-		 * Reshares for this user are shares where they are the owner.
642
-		 */
643
-		if ($reshares === false) {
644
-			//Special case for old shares created via the web UI
645
-			$or1 = $qb->expr()->andX(
646
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
647
-				$qb->expr()->isNull('uid_initiator')
648
-			);
649
-
650
-			$qb->andWhere(
651
-				$qb->expr()->orX(
652
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
653
-					$or1
654
-				)
655
-			);
656
-		} else {
657
-			$qb->andWhere(
658
-				$qb->expr()->orX(
659
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
660
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
661
-				)
662
-			);
663
-		}
664
-
665
-		if ($node !== null) {
666
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
667
-		}
668
-
669
-		if ($limit !== -1) {
670
-			$qb->setMaxResults($limit);
671
-		}
672
-
673
-		$qb->setFirstResult($offset);
674
-		$qb->orderBy('id');
675
-
676
-		$cursor = $qb->execute();
677
-		$shares = [];
678
-		while($data = $cursor->fetch()) {
679
-			$shares[] = $this->createShareObject($data);
680
-		}
681
-		$cursor->closeCursor();
682
-
683
-		return $shares;
684
-	}
685
-
686
-	/**
687
-	 * @inheritdoc
688
-	 */
689
-	public function getShareById($id, $recipientId = null) {
690
-		$qb = $this->dbConnection->getQueryBuilder();
691
-
692
-		$qb->select('*')
693
-			->from('share')
694
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
695
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
696
-
697
-		$cursor = $qb->execute();
698
-		$data = $cursor->fetch();
699
-		$cursor->closeCursor();
700
-
701
-		if ($data === false) {
702
-			throw new ShareNotFound('Can not find share with ID: ' . $id);
703
-		}
704
-
705
-		try {
706
-			$share = $this->createShareObject($data);
707
-		} catch (InvalidShare $e) {
708
-			throw new ShareNotFound();
709
-		}
710
-
711
-		return $share;
712
-	}
713
-
714
-	/**
715
-	 * Get shares for a given path
716
-	 *
717
-	 * @param \OCP\Files\Node $path
718
-	 * @return IShare[]
719
-	 */
720
-	public function getSharesByPath(Node $path) {
721
-		$qb = $this->dbConnection->getQueryBuilder();
722
-
723
-		$cursor = $qb->select('*')
724
-			->from('share')
725
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
726
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
727
-			->execute();
728
-
729
-		$shares = [];
730
-		while($data = $cursor->fetch()) {
731
-			$shares[] = $this->createShareObject($data);
732
-		}
733
-		$cursor->closeCursor();
734
-
735
-		return $shares;
736
-	}
737
-
738
-	/**
739
-	 * @inheritdoc
740
-	 */
741
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
742
-		/** @var IShare[] $shares */
743
-		$shares = [];
744
-
745
-		//Get shares directly with this user
746
-		$qb = $this->dbConnection->getQueryBuilder();
747
-		$qb->select('*')
748
-			->from('share');
749
-
750
-		// Order by id
751
-		$qb->orderBy('id');
752
-
753
-		// Set limit and offset
754
-		if ($limit !== -1) {
755
-			$qb->setMaxResults($limit);
756
-		}
757
-		$qb->setFirstResult($offset);
758
-
759
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
760
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
761
-
762
-		// Filter by node if provided
763
-		if ($node !== null) {
764
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
765
-		}
766
-
767
-		$cursor = $qb->execute();
768
-
769
-		while($data = $cursor->fetch()) {
770
-			$shares[] = $this->createShareObject($data);
771
-		}
772
-		$cursor->closeCursor();
773
-
774
-
775
-		return $shares;
776
-	}
777
-
778
-	/**
779
-	 * Get a share by token
780
-	 *
781
-	 * @param string $token
782
-	 * @return IShare
783
-	 * @throws ShareNotFound
784
-	 */
785
-	public function getShareByToken($token) {
786
-		$qb = $this->dbConnection->getQueryBuilder();
787
-
788
-		$cursor = $qb->select('*')
789
-			->from('share')
790
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
791
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
792
-			->execute();
793
-
794
-		$data = $cursor->fetch();
795
-
796
-		if ($data === false) {
797
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
798
-		}
799
-
800
-		try {
801
-			$share = $this->createShareObject($data);
802
-		} catch (InvalidShare $e) {
803
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
804
-		}
805
-
806
-		return $share;
807
-	}
808
-
809
-	/**
810
-	 * get database row of a give share
811
-	 *
812
-	 * @param $id
813
-	 * @return array
814
-	 * @throws ShareNotFound
815
-	 */
816
-	private function getRawShare($id) {
817
-
818
-		// Now fetch the inserted share and create a complete share object
819
-		$qb = $this->dbConnection->getQueryBuilder();
820
-		$qb->select('*')
821
-			->from('share')
822
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
823
-
824
-		$cursor = $qb->execute();
825
-		$data = $cursor->fetch();
826
-		$cursor->closeCursor();
827
-
828
-		if ($data === false) {
829
-			throw new ShareNotFound;
830
-		}
831
-
832
-		return $data;
833
-	}
834
-
835
-	/**
836
-	 * Create a share object from an database row
837
-	 *
838
-	 * @param array $data
839
-	 * @return IShare
840
-	 * @throws InvalidShare
841
-	 * @throws ShareNotFound
842
-	 */
843
-	private function createShareObject($data) {
844
-
845
-		$share = new Share($this->rootFolder, $this->userManager);
846
-		$share->setId((int)$data['id'])
847
-			->setShareType((int)$data['share_type'])
848
-			->setPermissions((int)$data['permissions'])
849
-			->setTarget($data['file_target'])
850
-			->setMailSend((bool)$data['mail_send'])
851
-			->setToken($data['token']);
852
-
853
-		$shareTime = new \DateTime();
854
-		$shareTime->setTimestamp((int)$data['stime']);
855
-		$share->setShareTime($shareTime);
856
-		$share->setSharedWith($data['share_with']);
857
-
858
-		if ($data['uid_initiator'] !== null) {
859
-			$share->setShareOwner($data['uid_owner']);
860
-			$share->setSharedBy($data['uid_initiator']);
861
-		} else {
862
-			//OLD SHARE
863
-			$share->setSharedBy($data['uid_owner']);
864
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
865
-
866
-			$owner = $path->getOwner();
867
-			$share->setShareOwner($owner->getUID());
868
-		}
869
-
870
-		$share->setNodeId((int)$data['file_source']);
871
-		$share->setNodeType($data['item_type']);
872
-
873
-		$share->setProviderId($this->identifier());
874
-
875
-		return $share;
876
-	}
877
-
878
-	/**
879
-	 * Get the node with file $id for $user
880
-	 *
881
-	 * @param string $userId
882
-	 * @param int $id
883
-	 * @return \OCP\Files\File|\OCP\Files\Folder
884
-	 * @throws InvalidShare
885
-	 */
886
-	private function getNode($userId, $id) {
887
-		try {
888
-			$userFolder = $this->rootFolder->getUserFolder($userId);
889
-		} catch (NotFoundException $e) {
890
-			throw new InvalidShare();
891
-		}
892
-
893
-		$nodes = $userFolder->getById($id);
894
-
895
-		if (empty($nodes)) {
896
-			throw new InvalidShare();
897
-		}
898
-
899
-		return $nodes[0];
900
-	}
901
-
902
-	/**
903
-	 * A user is deleted from the system
904
-	 * So clean up the relevant shares.
905
-	 *
906
-	 * @param string $uid
907
-	 * @param int $shareType
908
-	 */
909
-	public function userDeleted($uid, $shareType) {
910
-		//TODO: probabaly a good idea to send unshare info to remote servers
911
-
912
-		$qb = $this->dbConnection->getQueryBuilder();
913
-
914
-		$qb->delete('share')
915
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
916
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
917
-			->execute();
918
-	}
919
-
920
-	/**
921
-	 * This provider does not handle groups
922
-	 *
923
-	 * @param string $gid
924
-	 */
925
-	public function groupDeleted($gid) {
926
-		// We don't handle groups here
927
-	}
928
-
929
-	/**
930
-	 * This provider does not handle groups
931
-	 *
932
-	 * @param string $uid
933
-	 * @param string $gid
934
-	 */
935
-	public function userDeletedFromGroup($uid, $gid) {
936
-		// We don't handle groups here
937
-	}
938
-
939
-	/**
940
-	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
941
-	 *
942
-	 * @return bool
943
-	 */
944
-	public function isOutgoingServer2serverShareEnabled() {
945
-		if ($this->gsConfig->onlyInternalFederation()) {
946
-			return false;
947
-		}
948
-		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
949
-		return ($result === 'yes');
950
-	}
951
-
952
-	/**
953
-	 * check if users are allowed to mount public links from other Nextclouds
954
-	 *
955
-	 * @return bool
956
-	 */
957
-	public function isIncomingServer2serverShareEnabled() {
958
-		if ($this->gsConfig->onlyInternalFederation()) {
959
-			return false;
960
-		}
961
-		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
962
-		return ($result === 'yes');
963
-	}
964
-
965
-	/**
966
-	 * Check if querying sharees on the lookup server is enabled
967
-	 *
968
-	 * @return bool
969
-	 */
970
-	public function isLookupServerQueriesEnabled() {
971
-		// in a global scale setup we should always query the lookup server
972
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
973
-			return true;
974
-		}
975
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
976
-		return ($result === 'yes');
977
-	}
978
-
979
-
980
-	/**
981
-	 * Check if it is allowed to publish user specific data to the lookup server
982
-	 *
983
-	 * @return bool
984
-	 */
985
-	public function isLookupServerUploadEnabled() {
986
-		// in a global scale setup the admin is responsible to keep the lookup server up-to-date
987
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
988
-			return false;
989
-		}
990
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
991
-		return ($result === 'yes');
992
-	}
993
-
994
-	/**
995
-	 * @inheritdoc
996
-	 */
997
-	public function getAccessList($nodes, $currentAccess) {
998
-		$ids = [];
999
-		foreach ($nodes as $node) {
1000
-			$ids[] = $node->getId();
1001
-		}
1002
-
1003
-		$qb = $this->dbConnection->getQueryBuilder();
1004
-		$qb->select('share_with', 'token', 'file_source')
1005
-			->from('share')
1006
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1007
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1008
-			->andWhere($qb->expr()->orX(
1009
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1010
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1011
-			));
1012
-		$cursor = $qb->execute();
1013
-
1014
-		if ($currentAccess === false) {
1015
-			$remote = $cursor->fetch() !== false;
1016
-			$cursor->closeCursor();
1017
-
1018
-			return ['remote' => $remote];
1019
-		}
1020
-
1021
-		$remote = [];
1022
-		while ($row = $cursor->fetch()) {
1023
-			$remote[$row['share_with']] = [
1024
-				'node_id' => $row['file_source'],
1025
-				'token' => $row['token'],
1026
-			];
1027
-		}
1028
-		$cursor->closeCursor();
1029
-
1030
-		return ['remote' => $remote];
1031
-	}
477
+        return $share;
478
+    }
479
+
480
+    /**
481
+     * Get all children of this share
482
+     *
483
+     * @param IShare $parent
484
+     * @return IShare[]
485
+     */
486
+    public function getChildren(IShare $parent) {
487
+        $children = [];
488
+
489
+        $qb = $this->dbConnection->getQueryBuilder();
490
+        $qb->select('*')
491
+            ->from('share')
492
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
493
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
494
+            ->orderBy('id');
495
+
496
+        $cursor = $qb->execute();
497
+        while($data = $cursor->fetch()) {
498
+            $children[] = $this->createShareObject($data);
499
+        }
500
+        $cursor->closeCursor();
501
+
502
+        return $children;
503
+    }
504
+
505
+    /**
506
+     * Delete a share (owner unShares the file)
507
+     *
508
+     * @param IShare $share
509
+     * @throws ShareNotFound
510
+     * @throws \OC\HintException
511
+     */
512
+    public function delete(IShare $share) {
513
+
514
+        list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
515
+
516
+        // if the local user is the owner we can send the unShare request directly...
517
+        if ($this->userManager->userExists($share->getShareOwner())) {
518
+            $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
519
+            $this->revokeShare($share, true);
520
+        } else { // ... if not we need to correct ID for the unShare request
521
+            $remoteId = $this->getRemoteId($share);
522
+            $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
523
+            $this->revokeShare($share, false);
524
+        }
525
+
526
+        // only remove the share when all messages are send to not lose information
527
+        // about the share to early
528
+        $this->removeShareFromTable($share);
529
+    }
530
+
531
+    /**
532
+     * in case of a re-share we need to send the other use (initiator or owner)
533
+     * a message that the file was unshared
534
+     *
535
+     * @param IShare $share
536
+     * @param bool $isOwner the user can either be the owner or the user who re-sahred it
537
+     * @throws ShareNotFound
538
+     * @throws \OC\HintException
539
+     */
540
+    protected function revokeShare($share, $isOwner) {
541
+        // also send a unShare request to the initiator, if this is a different user than the owner
542
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
543
+            if ($isOwner) {
544
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
545
+            } else {
546
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
547
+            }
548
+            $remoteId = $this->getRemoteId($share);
549
+            $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
550
+        }
551
+    }
552
+
553
+    /**
554
+     * remove share from table
555
+     *
556
+     * @param IShare $share
557
+     */
558
+    public function removeShareFromTable(IShare $share) {
559
+        $this->removeShareFromTableById($share->getId());
560
+    }
561
+
562
+    /**
563
+     * remove share from table
564
+     *
565
+     * @param string $shareId
566
+     */
567
+    private function removeShareFromTableById($shareId) {
568
+        $qb = $this->dbConnection->getQueryBuilder();
569
+        $qb->delete('share')
570
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
571
+        $qb->execute();
572
+
573
+        $qb->delete('federated_reshares')
574
+            ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
575
+        $qb->execute();
576
+    }
577
+
578
+    /**
579
+     * @inheritdoc
580
+     */
581
+    public function deleteFromSelf(IShare $share, $recipient) {
582
+        // nothing to do here. Technically deleteFromSelf in the context of federated
583
+        // shares is a umount of a external storage. This is handled here
584
+        // apps/files_sharing/lib/external/manager.php
585
+        // TODO move this code over to this app
586
+    }
587
+
588
+
589
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
590
+        $qb = $this->dbConnection->getQueryBuilder();
591
+        $qb->select('*')
592
+            ->from('share', 's')
593
+            ->andWhere($qb->expr()->orX(
594
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
595
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
596
+            ))
597
+            ->andWhere(
598
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
599
+            );
600
+
601
+        /**
602
+         * Reshares for this user are shares where they are the owner.
603
+         */
604
+        if ($reshares === false) {
605
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
606
+        } else {
607
+            $qb->andWhere(
608
+                $qb->expr()->orX(
609
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
610
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
611
+                )
612
+            );
613
+        }
614
+
615
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
616
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
617
+
618
+        $qb->orderBy('id');
619
+
620
+        $cursor = $qb->execute();
621
+        $shares = [];
622
+        while ($data = $cursor->fetch()) {
623
+            $shares[$data['fileid']][] = $this->createShareObject($data);
624
+        }
625
+        $cursor->closeCursor();
626
+
627
+        return $shares;
628
+    }
629
+
630
+    /**
631
+     * @inheritdoc
632
+     */
633
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
634
+        $qb = $this->dbConnection->getQueryBuilder();
635
+        $qb->select('*')
636
+            ->from('share');
637
+
638
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
639
+
640
+        /**
641
+         * Reshares for this user are shares where they are the owner.
642
+         */
643
+        if ($reshares === false) {
644
+            //Special case for old shares created via the web UI
645
+            $or1 = $qb->expr()->andX(
646
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
647
+                $qb->expr()->isNull('uid_initiator')
648
+            );
649
+
650
+            $qb->andWhere(
651
+                $qb->expr()->orX(
652
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
653
+                    $or1
654
+                )
655
+            );
656
+        } else {
657
+            $qb->andWhere(
658
+                $qb->expr()->orX(
659
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
660
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
661
+                )
662
+            );
663
+        }
664
+
665
+        if ($node !== null) {
666
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
667
+        }
668
+
669
+        if ($limit !== -1) {
670
+            $qb->setMaxResults($limit);
671
+        }
672
+
673
+        $qb->setFirstResult($offset);
674
+        $qb->orderBy('id');
675
+
676
+        $cursor = $qb->execute();
677
+        $shares = [];
678
+        while($data = $cursor->fetch()) {
679
+            $shares[] = $this->createShareObject($data);
680
+        }
681
+        $cursor->closeCursor();
682
+
683
+        return $shares;
684
+    }
685
+
686
+    /**
687
+     * @inheritdoc
688
+     */
689
+    public function getShareById($id, $recipientId = null) {
690
+        $qb = $this->dbConnection->getQueryBuilder();
691
+
692
+        $qb->select('*')
693
+            ->from('share')
694
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
695
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
696
+
697
+        $cursor = $qb->execute();
698
+        $data = $cursor->fetch();
699
+        $cursor->closeCursor();
700
+
701
+        if ($data === false) {
702
+            throw new ShareNotFound('Can not find share with ID: ' . $id);
703
+        }
704
+
705
+        try {
706
+            $share = $this->createShareObject($data);
707
+        } catch (InvalidShare $e) {
708
+            throw new ShareNotFound();
709
+        }
710
+
711
+        return $share;
712
+    }
713
+
714
+    /**
715
+     * Get shares for a given path
716
+     *
717
+     * @param \OCP\Files\Node $path
718
+     * @return IShare[]
719
+     */
720
+    public function getSharesByPath(Node $path) {
721
+        $qb = $this->dbConnection->getQueryBuilder();
722
+
723
+        $cursor = $qb->select('*')
724
+            ->from('share')
725
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
726
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
727
+            ->execute();
728
+
729
+        $shares = [];
730
+        while($data = $cursor->fetch()) {
731
+            $shares[] = $this->createShareObject($data);
732
+        }
733
+        $cursor->closeCursor();
734
+
735
+        return $shares;
736
+    }
737
+
738
+    /**
739
+     * @inheritdoc
740
+     */
741
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
742
+        /** @var IShare[] $shares */
743
+        $shares = [];
744
+
745
+        //Get shares directly with this user
746
+        $qb = $this->dbConnection->getQueryBuilder();
747
+        $qb->select('*')
748
+            ->from('share');
749
+
750
+        // Order by id
751
+        $qb->orderBy('id');
752
+
753
+        // Set limit and offset
754
+        if ($limit !== -1) {
755
+            $qb->setMaxResults($limit);
756
+        }
757
+        $qb->setFirstResult($offset);
758
+
759
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
760
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
761
+
762
+        // Filter by node if provided
763
+        if ($node !== null) {
764
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
765
+        }
766
+
767
+        $cursor = $qb->execute();
768
+
769
+        while($data = $cursor->fetch()) {
770
+            $shares[] = $this->createShareObject($data);
771
+        }
772
+        $cursor->closeCursor();
773
+
774
+
775
+        return $shares;
776
+    }
777
+
778
+    /**
779
+     * Get a share by token
780
+     *
781
+     * @param string $token
782
+     * @return IShare
783
+     * @throws ShareNotFound
784
+     */
785
+    public function getShareByToken($token) {
786
+        $qb = $this->dbConnection->getQueryBuilder();
787
+
788
+        $cursor = $qb->select('*')
789
+            ->from('share')
790
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
791
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
792
+            ->execute();
793
+
794
+        $data = $cursor->fetch();
795
+
796
+        if ($data === false) {
797
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
798
+        }
799
+
800
+        try {
801
+            $share = $this->createShareObject($data);
802
+        } catch (InvalidShare $e) {
803
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
804
+        }
805
+
806
+        return $share;
807
+    }
808
+
809
+    /**
810
+     * get database row of a give share
811
+     *
812
+     * @param $id
813
+     * @return array
814
+     * @throws ShareNotFound
815
+     */
816
+    private function getRawShare($id) {
817
+
818
+        // Now fetch the inserted share and create a complete share object
819
+        $qb = $this->dbConnection->getQueryBuilder();
820
+        $qb->select('*')
821
+            ->from('share')
822
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
823
+
824
+        $cursor = $qb->execute();
825
+        $data = $cursor->fetch();
826
+        $cursor->closeCursor();
827
+
828
+        if ($data === false) {
829
+            throw new ShareNotFound;
830
+        }
831
+
832
+        return $data;
833
+    }
834
+
835
+    /**
836
+     * Create a share object from an database row
837
+     *
838
+     * @param array $data
839
+     * @return IShare
840
+     * @throws InvalidShare
841
+     * @throws ShareNotFound
842
+     */
843
+    private function createShareObject($data) {
844
+
845
+        $share = new Share($this->rootFolder, $this->userManager);
846
+        $share->setId((int)$data['id'])
847
+            ->setShareType((int)$data['share_type'])
848
+            ->setPermissions((int)$data['permissions'])
849
+            ->setTarget($data['file_target'])
850
+            ->setMailSend((bool)$data['mail_send'])
851
+            ->setToken($data['token']);
852
+
853
+        $shareTime = new \DateTime();
854
+        $shareTime->setTimestamp((int)$data['stime']);
855
+        $share->setShareTime($shareTime);
856
+        $share->setSharedWith($data['share_with']);
857
+
858
+        if ($data['uid_initiator'] !== null) {
859
+            $share->setShareOwner($data['uid_owner']);
860
+            $share->setSharedBy($data['uid_initiator']);
861
+        } else {
862
+            //OLD SHARE
863
+            $share->setSharedBy($data['uid_owner']);
864
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
865
+
866
+            $owner = $path->getOwner();
867
+            $share->setShareOwner($owner->getUID());
868
+        }
869
+
870
+        $share->setNodeId((int)$data['file_source']);
871
+        $share->setNodeType($data['item_type']);
872
+
873
+        $share->setProviderId($this->identifier());
874
+
875
+        return $share;
876
+    }
877
+
878
+    /**
879
+     * Get the node with file $id for $user
880
+     *
881
+     * @param string $userId
882
+     * @param int $id
883
+     * @return \OCP\Files\File|\OCP\Files\Folder
884
+     * @throws InvalidShare
885
+     */
886
+    private function getNode($userId, $id) {
887
+        try {
888
+            $userFolder = $this->rootFolder->getUserFolder($userId);
889
+        } catch (NotFoundException $e) {
890
+            throw new InvalidShare();
891
+        }
892
+
893
+        $nodes = $userFolder->getById($id);
894
+
895
+        if (empty($nodes)) {
896
+            throw new InvalidShare();
897
+        }
898
+
899
+        return $nodes[0];
900
+    }
901
+
902
+    /**
903
+     * A user is deleted from the system
904
+     * So clean up the relevant shares.
905
+     *
906
+     * @param string $uid
907
+     * @param int $shareType
908
+     */
909
+    public function userDeleted($uid, $shareType) {
910
+        //TODO: probabaly a good idea to send unshare info to remote servers
911
+
912
+        $qb = $this->dbConnection->getQueryBuilder();
913
+
914
+        $qb->delete('share')
915
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
916
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
917
+            ->execute();
918
+    }
919
+
920
+    /**
921
+     * This provider does not handle groups
922
+     *
923
+     * @param string $gid
924
+     */
925
+    public function groupDeleted($gid) {
926
+        // We don't handle groups here
927
+    }
928
+
929
+    /**
930
+     * This provider does not handle groups
931
+     *
932
+     * @param string $uid
933
+     * @param string $gid
934
+     */
935
+    public function userDeletedFromGroup($uid, $gid) {
936
+        // We don't handle groups here
937
+    }
938
+
939
+    /**
940
+     * check if users from other Nextcloud instances are allowed to mount public links share by this instance
941
+     *
942
+     * @return bool
943
+     */
944
+    public function isOutgoingServer2serverShareEnabled() {
945
+        if ($this->gsConfig->onlyInternalFederation()) {
946
+            return false;
947
+        }
948
+        $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
949
+        return ($result === 'yes');
950
+    }
951
+
952
+    /**
953
+     * check if users are allowed to mount public links from other Nextclouds
954
+     *
955
+     * @return bool
956
+     */
957
+    public function isIncomingServer2serverShareEnabled() {
958
+        if ($this->gsConfig->onlyInternalFederation()) {
959
+            return false;
960
+        }
961
+        $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
962
+        return ($result === 'yes');
963
+    }
964
+
965
+    /**
966
+     * Check if querying sharees on the lookup server is enabled
967
+     *
968
+     * @return bool
969
+     */
970
+    public function isLookupServerQueriesEnabled() {
971
+        // in a global scale setup we should always query the lookup server
972
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
973
+            return true;
974
+        }
975
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
976
+        return ($result === 'yes');
977
+    }
978
+
979
+
980
+    /**
981
+     * Check if it is allowed to publish user specific data to the lookup server
982
+     *
983
+     * @return bool
984
+     */
985
+    public function isLookupServerUploadEnabled() {
986
+        // in a global scale setup the admin is responsible to keep the lookup server up-to-date
987
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
988
+            return false;
989
+        }
990
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
991
+        return ($result === 'yes');
992
+    }
993
+
994
+    /**
995
+     * @inheritdoc
996
+     */
997
+    public function getAccessList($nodes, $currentAccess) {
998
+        $ids = [];
999
+        foreach ($nodes as $node) {
1000
+            $ids[] = $node->getId();
1001
+        }
1002
+
1003
+        $qb = $this->dbConnection->getQueryBuilder();
1004
+        $qb->select('share_with', 'token', 'file_source')
1005
+            ->from('share')
1006
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1007
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1008
+            ->andWhere($qb->expr()->orX(
1009
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1010
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1011
+            ));
1012
+        $cursor = $qb->execute();
1013
+
1014
+        if ($currentAccess === false) {
1015
+            $remote = $cursor->fetch() !== false;
1016
+            $cursor->closeCursor();
1017
+
1018
+            return ['remote' => $remote];
1019
+        }
1020
+
1021
+        $remote = [];
1022
+        while ($row = $cursor->fetch()) {
1023
+            $remote[$row['share_with']] = [
1024
+                'node_id' => $row['file_source'],
1025
+                'token' => $row['token'],
1026
+            ];
1027
+        }
1028
+        $cursor->closeCursor();
1029
+
1030
+        return ['remote' => $remote];
1031
+    }
1032 1032
 }
Please login to merge, or discard this patch.
apps/files_trashbin/lib/Storage.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -110,7 +110,7 @@
 block discarded – undo
110 110
 	 * check if it is a file located in data/user/files only files in the
111 111
 	 * 'files' directory should be moved to the trash
112 112
 	 *
113
-	 * @param $path
113
+	 * @param string $path
114 114
 	 * @return bool
115 115
 	 */
116 116
 	protected function shouldMoveToTrash($path){
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -114,7 +114,7 @@  discard block
 block discarded – undo
114 114
 			if ($currentUser) {
115 115
 				$currentUserId = $currentUser->getUID();
116 116
 
117
-				$view = new View($currentUserId . '/files');
117
+				$view = new View($currentUserId.'/files');
118 118
 				$fileInfo = $view->getFileInfo($oldPath);
119 119
 				if ($fileInfo) {
120 120
 					$sourceStorage = $fileInfo->getStorage();
@@ -137,8 +137,8 @@  discard block
 block discarded – undo
137 137
 			]);
138 138
 		}
139 139
 
140
-		if($fileMovedOutOfSharedFolder) {
141
-			self::$moveOutOfSharedFolder['/' . $currentUserId . '/files' . $oldPath] = true;
140
+		if ($fileMovedOutOfSharedFolder) {
141
+			self::$moveOutOfSharedFolder['/'.$currentUserId.'/files'.$oldPath] = true;
142 142
 		} else {
143 143
 			self::$disableTrash = true;
144 144
 		}
@@ -178,16 +178,16 @@  discard block
 block discarded – undo
178 178
 	 */
179 179
 	public function unlink($path) {
180 180
 		try {
181
-			if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
181
+			if (isset(self::$moveOutOfSharedFolder[$this->mountPoint.$path])) {
182 182
 				$result = $this->doDelete($path, 'unlink', true);
183
-				unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
183
+				unset(self::$moveOutOfSharedFolder[$this->mountPoint.$path]);
184 184
 			} else {
185 185
 				$result = $this->doDelete($path, 'unlink');
186 186
 			}
187 187
 		} catch (GenericEncryptionException $e) {
188 188
 			// in case of a encryption exception we delete the file right away
189 189
 			$this->logger->info(
190
-				"Can't move file" .  $path .
190
+				"Can't move file".$path.
191 191
 				"to the trash bin, therefore it was deleted right away");
192 192
 
193 193
 			$result = $this->storage->unlink($path);
@@ -204,9 +204,9 @@  discard block
 block discarded – undo
204 204
 	 * @return bool true if the operation succeeded, false otherwise
205 205
 	 */
206 206
 	public function rmdir($path) {
207
-		if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
207
+		if (isset(self::$moveOutOfSharedFolder[$this->mountPoint.$path])) {
208 208
 			$result = $this->doDelete($path, 'rmdir', true);
209
-			unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
209
+			unset(self::$moveOutOfSharedFolder[$this->mountPoint.$path]);
210 210
 		} else {
211 211
 			$result = $this->doDelete($path, 'rmdir');
212 212
 		}
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
 	 * @param $path
222 222
 	 * @return bool
223 223
 	 */
224
-	protected function shouldMoveToTrash($path){
224
+	protected function shouldMoveToTrash($path) {
225 225
 
226 226
 		// check if there is a app which want to disable the trash bin for this file
227 227
 		$fileId = $this->storage->getCache()->getId($path);
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
 			}
235 235
 		}
236 236
 
237
-		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
237
+		$normalized = Filesystem::normalizePath($this->mountPoint.'/'.$path);
238 238
 		$parts = explode('/', $normalized);
239 239
 		if (count($parts) < 4) {
240 240
 			return false;
@@ -282,7 +282,7 @@  discard block
 block discarded – undo
282 282
 			return false;
283 283
 		}
284 284
 
285
-		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path, true, false, true);
285
+		$normalized = Filesystem::normalizePath($this->mountPoint.'/'.$path, true, false, true);
286 286
 		$result = true;
287 287
 		$view = Filesystem::getView();
288 288
 		if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
 	 * Setup the storate wrapper callback
311 311
 	 */
312 312
 	public static function setupStorage() {
313
-		\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
313
+		\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function($mountPoint, $storage) {
314 314
 			return new \OCA\Files_Trashbin\Storage(
315 315
 				array('storage' => $storage, 'mountPoint' => $mountPoint),
316 316
 				\OC::$server->getUserManager(),
Please login to merge, or discard this patch.
Indentation   +279 added lines, -279 removed lines patch added patch discarded remove patch
@@ -40,284 +40,284 @@
 block discarded – undo
40 40
 
41 41
 class Storage extends Wrapper {
42 42
 
43
-	private $mountPoint;
44
-	// remember already deleted files to avoid infinite loops if the trash bin
45
-	// move files across storages
46
-	private $deletedFiles = array();
47
-
48
-	/**
49
-	 * Disable trash logic
50
-	 *
51
-	 * @var bool
52
-	 */
53
-	private static $disableTrash = false;
54
-
55
-	/**
56
-	 * remember which file/folder was moved out of s shared folder
57
-	 * in this case we want to add a copy to the owners trash bin
58
-	 *
59
-	 * @var array
60
-	 */
61
-	private static $moveOutOfSharedFolder = [];
62
-
63
-	/** @var  IUserManager */
64
-	private $userManager;
65
-
66
-	/** @var ILogger */
67
-	private $logger;
68
-
69
-	/** @var EventDispatcher */
70
-	private $eventDispatcher;
71
-
72
-	/** @var IRootFolder */
73
-	private $rootFolder;
74
-
75
-	/**
76
-	 * Storage constructor.
77
-	 *
78
-	 * @param array $parameters
79
-	 * @param IUserManager|null $userManager
80
-	 * @param ILogger|null $logger
81
-	 * @param EventDispatcher|null $eventDispatcher
82
-	 * @param IRootFolder|null $rootFolder
83
-	 */
84
-	public function __construct($parameters,
85
-								IUserManager $userManager = null,
86
-								ILogger $logger = null,
87
-								EventDispatcher $eventDispatcher = null,
88
-								IRootFolder $rootFolder = null) {
89
-		$this->mountPoint = $parameters['mountPoint'];
90
-		$this->userManager = $userManager;
91
-		$this->logger = $logger;
92
-		$this->eventDispatcher = $eventDispatcher;
93
-		$this->rootFolder = $rootFolder;
94
-		parent::__construct($parameters);
95
-	}
96
-
97
-	/**
98
-	 * @internal
99
-	 */
100
-	public static function preRenameHook($params) {
101
-		// in cross-storage cases, a rename is a copy + unlink,
102
-		// that last unlink must not go to trash, only exception:
103
-		// if the file was moved from a shared storage to a local folder,
104
-		// in this case the owner should get a copy in his trash bin so that
105
-		// they can restore the files again
106
-
107
-		$oldPath = $params['oldpath'];
108
-		$newPath = dirname($params['newpath']);
109
-		$currentUser = \OC::$server->getUserSession()->getUser();
110
-
111
-		$fileMovedOutOfSharedFolder = false;
112
-
113
-		try {
114
-			if ($currentUser) {
115
-				$currentUserId = $currentUser->getUID();
116
-
117
-				$view = new View($currentUserId . '/files');
118
-				$fileInfo = $view->getFileInfo($oldPath);
119
-				if ($fileInfo) {
120
-					$sourceStorage = $fileInfo->getStorage();
121
-					$sourceOwner = $view->getOwner($oldPath);
122
-					$targetOwner = $view->getOwner($newPath);
123
-
124
-					if ($sourceOwner !== $targetOwner
125
-						&& $sourceStorage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')
126
-					) {
127
-						$fileMovedOutOfSharedFolder = true;
128
-					}
129
-				}
130
-			}
131
-		} catch (\Exception $e) {
132
-			// do nothing, in this case we just disable the trashbin and continue
133
-			\OC::$server->getLogger()->logException($e, [
134
-				'message' => 'Trashbin storage could not check if a file was moved out of a shared folder.',
135
-				'level' => ILogger::DEBUG,
136
-				'app' => 'files_trashbin',
137
-			]);
138
-		}
139
-
140
-		if($fileMovedOutOfSharedFolder) {
141
-			self::$moveOutOfSharedFolder['/' . $currentUserId . '/files' . $oldPath] = true;
142
-		} else {
143
-			self::$disableTrash = true;
144
-		}
145
-
146
-	}
147
-
148
-	/**
149
-	 * @internal
150
-	 */
151
-	public static function postRenameHook($params) {
152
-		self::$disableTrash = false;
153
-	}
154
-
155
-	/**
156
-	 * Rename path1 to path2 by calling the wrapped storage.
157
-	 *
158
-	 * @param string $path1 first path
159
-	 * @param string $path2 second path
160
-	 * @return bool
161
-	 */
162
-	public function rename($path1, $path2) {
163
-		$result = $this->storage->rename($path1, $path2);
164
-		if ($result === false) {
165
-			// when rename failed, the post_rename hook isn't triggered,
166
-			// but we still want to reenable the trash logic
167
-			self::$disableTrash = false;
168
-		}
169
-		return $result;
170
-	}
171
-
172
-	/**
173
-	 * Deletes the given file by moving it into the trashbin.
174
-	 *
175
-	 * @param string $path path of file or folder to delete
176
-	 *
177
-	 * @return bool true if the operation succeeded, false otherwise
178
-	 */
179
-	public function unlink($path) {
180
-		try {
181
-			if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
182
-				$result = $this->doDelete($path, 'unlink', true);
183
-				unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
184
-			} else {
185
-				$result = $this->doDelete($path, 'unlink');
186
-			}
187
-		} catch (GenericEncryptionException $e) {
188
-			// in case of a encryption exception we delete the file right away
189
-			$this->logger->info(
190
-				"Can't move file" .  $path .
191
-				"to the trash bin, therefore it was deleted right away");
192
-
193
-			$result = $this->storage->unlink($path);
194
-		}
195
-
196
-		return $result;
197
-	}
198
-
199
-	/**
200
-	 * Deletes the given folder by moving it into the trashbin.
201
-	 *
202
-	 * @param string $path path of folder to delete
203
-	 *
204
-	 * @return bool true if the operation succeeded, false otherwise
205
-	 */
206
-	public function rmdir($path) {
207
-		if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
208
-			$result = $this->doDelete($path, 'rmdir', true);
209
-			unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
210
-		} else {
211
-			$result = $this->doDelete($path, 'rmdir');
212
-		}
213
-
214
-		return $result;
215
-	}
216
-
217
-	/**
218
-	 * check if it is a file located in data/user/files only files in the
219
-	 * 'files' directory should be moved to the trash
220
-	 *
221
-	 * @param $path
222
-	 * @return bool
223
-	 */
224
-	protected function shouldMoveToTrash($path){
225
-
226
-		// check if there is a app which want to disable the trash bin for this file
227
-		$fileId = $this->storage->getCache()->getId($path);
228
-		$nodes = $this->rootFolder->getById($fileId);
229
-		foreach ($nodes as $node) {
230
-			$event = $this->createMoveToTrashEvent($node);
231
-			$this->eventDispatcher->dispatch('OCA\Files_Trashbin::moveToTrash', $event);
232
-			if ($event->shouldMoveToTrashBin() === false) {
233
-				return false;
234
-			}
235
-		}
236
-
237
-		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
238
-		$parts = explode('/', $normalized);
239
-		if (count($parts) < 4) {
240
-			return false;
241
-		}
242
-
243
-		if ($parts[2] === 'files' && $this->userManager->userExists($parts[1])) {
244
-			return true;
245
-		}
246
-
247
-		return false;
248
-	}
249
-
250
-	/**
251
-	 * get move to trash event
252
-	 *
253
-	 * @param Node $node
254
-	 * @return MoveToTrashEvent
255
-	 */
256
-	protected function createMoveToTrashEvent(Node $node) {
257
-		return new MoveToTrashEvent($node);
258
-	}
259
-
260
-	/**
261
-	 * Run the delete operation with the given method
262
-	 *
263
-	 * @param string $path path of file or folder to delete
264
-	 * @param string $method either "unlink" or "rmdir"
265
-	 * @param bool $ownerOnly delete for owner only (if file gets moved out of a shared folder)
266
-	 *
267
-	 * @return bool true if the operation succeeded, false otherwise
268
-	 */
269
-	private function doDelete($path, $method, $ownerOnly = false) {
270
-		if (self::$disableTrash
271
-			|| !\OC::$server->getAppManager()->isEnabledForUser('files_trashbin')
272
-			|| (pathinfo($path, PATHINFO_EXTENSION) === 'part')
273
-			|| $this->shouldMoveToTrash($path) === false
274
-		) {
275
-			return call_user_func_array([$this->storage, $method], [$path]);
276
-		}
277
-
278
-		// check permissions before we continue, this is especially important for
279
-		// shared files
280
-		if (!$this->isDeletable($path)) {
281
-			return false;
282
-		}
283
-
284
-		$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path, true, false, true);
285
-		$result = true;
286
-		$view = Filesystem::getView();
287
-		if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
288
-			$this->deletedFiles[$normalized] = $normalized;
289
-			if ($filesPath = $view->getRelativePath($normalized)) {
290
-				$filesPath = trim($filesPath, '/');
291
-				$result = \OCA\Files_Trashbin\Trashbin::move2trash($filesPath, $ownerOnly);
292
-				// in cross-storage cases the file will be copied
293
-				// but not deleted, so we delete it here
294
-				if ($result) {
295
-					call_user_func_array([$this->storage, $method], [$path]);
296
-				}
297
-			} else {
298
-				$result = call_user_func_array([$this->storage, $method], [$path]);
299
-			}
300
-			unset($this->deletedFiles[$normalized]);
301
-		} else if ($this->storage->file_exists($path)) {
302
-			$result = call_user_func_array([$this->storage, $method], [$path]);
303
-		}
304
-
305
-		return $result;
306
-	}
307
-
308
-	/**
309
-	 * Setup the storate wrapper callback
310
-	 */
311
-	public static function setupStorage() {
312
-		\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
313
-			return new \OCA\Files_Trashbin\Storage(
314
-				array('storage' => $storage, 'mountPoint' => $mountPoint),
315
-				\OC::$server->getUserManager(),
316
-				\OC::$server->getLogger(),
317
-				\OC::$server->getEventDispatcher(),
318
-				\OC::$server->getLazyRootFolder()
319
-			);
320
-		}, 1);
321
-	}
43
+    private $mountPoint;
44
+    // remember already deleted files to avoid infinite loops if the trash bin
45
+    // move files across storages
46
+    private $deletedFiles = array();
47
+
48
+    /**
49
+     * Disable trash logic
50
+     *
51
+     * @var bool
52
+     */
53
+    private static $disableTrash = false;
54
+
55
+    /**
56
+     * remember which file/folder was moved out of s shared folder
57
+     * in this case we want to add a copy to the owners trash bin
58
+     *
59
+     * @var array
60
+     */
61
+    private static $moveOutOfSharedFolder = [];
62
+
63
+    /** @var  IUserManager */
64
+    private $userManager;
65
+
66
+    /** @var ILogger */
67
+    private $logger;
68
+
69
+    /** @var EventDispatcher */
70
+    private $eventDispatcher;
71
+
72
+    /** @var IRootFolder */
73
+    private $rootFolder;
74
+
75
+    /**
76
+     * Storage constructor.
77
+     *
78
+     * @param array $parameters
79
+     * @param IUserManager|null $userManager
80
+     * @param ILogger|null $logger
81
+     * @param EventDispatcher|null $eventDispatcher
82
+     * @param IRootFolder|null $rootFolder
83
+     */
84
+    public function __construct($parameters,
85
+                                IUserManager $userManager = null,
86
+                                ILogger $logger = null,
87
+                                EventDispatcher $eventDispatcher = null,
88
+                                IRootFolder $rootFolder = null) {
89
+        $this->mountPoint = $parameters['mountPoint'];
90
+        $this->userManager = $userManager;
91
+        $this->logger = $logger;
92
+        $this->eventDispatcher = $eventDispatcher;
93
+        $this->rootFolder = $rootFolder;
94
+        parent::__construct($parameters);
95
+    }
96
+
97
+    /**
98
+     * @internal
99
+     */
100
+    public static function preRenameHook($params) {
101
+        // in cross-storage cases, a rename is a copy + unlink,
102
+        // that last unlink must not go to trash, only exception:
103
+        // if the file was moved from a shared storage to a local folder,
104
+        // in this case the owner should get a copy in his trash bin so that
105
+        // they can restore the files again
106
+
107
+        $oldPath = $params['oldpath'];
108
+        $newPath = dirname($params['newpath']);
109
+        $currentUser = \OC::$server->getUserSession()->getUser();
110
+
111
+        $fileMovedOutOfSharedFolder = false;
112
+
113
+        try {
114
+            if ($currentUser) {
115
+                $currentUserId = $currentUser->getUID();
116
+
117
+                $view = new View($currentUserId . '/files');
118
+                $fileInfo = $view->getFileInfo($oldPath);
119
+                if ($fileInfo) {
120
+                    $sourceStorage = $fileInfo->getStorage();
121
+                    $sourceOwner = $view->getOwner($oldPath);
122
+                    $targetOwner = $view->getOwner($newPath);
123
+
124
+                    if ($sourceOwner !== $targetOwner
125
+                        && $sourceStorage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')
126
+                    ) {
127
+                        $fileMovedOutOfSharedFolder = true;
128
+                    }
129
+                }
130
+            }
131
+        } catch (\Exception $e) {
132
+            // do nothing, in this case we just disable the trashbin and continue
133
+            \OC::$server->getLogger()->logException($e, [
134
+                'message' => 'Trashbin storage could not check if a file was moved out of a shared folder.',
135
+                'level' => ILogger::DEBUG,
136
+                'app' => 'files_trashbin',
137
+            ]);
138
+        }
139
+
140
+        if($fileMovedOutOfSharedFolder) {
141
+            self::$moveOutOfSharedFolder['/' . $currentUserId . '/files' . $oldPath] = true;
142
+        } else {
143
+            self::$disableTrash = true;
144
+        }
145
+
146
+    }
147
+
148
+    /**
149
+     * @internal
150
+     */
151
+    public static function postRenameHook($params) {
152
+        self::$disableTrash = false;
153
+    }
154
+
155
+    /**
156
+     * Rename path1 to path2 by calling the wrapped storage.
157
+     *
158
+     * @param string $path1 first path
159
+     * @param string $path2 second path
160
+     * @return bool
161
+     */
162
+    public function rename($path1, $path2) {
163
+        $result = $this->storage->rename($path1, $path2);
164
+        if ($result === false) {
165
+            // when rename failed, the post_rename hook isn't triggered,
166
+            // but we still want to reenable the trash logic
167
+            self::$disableTrash = false;
168
+        }
169
+        return $result;
170
+    }
171
+
172
+    /**
173
+     * Deletes the given file by moving it into the trashbin.
174
+     *
175
+     * @param string $path path of file or folder to delete
176
+     *
177
+     * @return bool true if the operation succeeded, false otherwise
178
+     */
179
+    public function unlink($path) {
180
+        try {
181
+            if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
182
+                $result = $this->doDelete($path, 'unlink', true);
183
+                unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
184
+            } else {
185
+                $result = $this->doDelete($path, 'unlink');
186
+            }
187
+        } catch (GenericEncryptionException $e) {
188
+            // in case of a encryption exception we delete the file right away
189
+            $this->logger->info(
190
+                "Can't move file" .  $path .
191
+                "to the trash bin, therefore it was deleted right away");
192
+
193
+            $result = $this->storage->unlink($path);
194
+        }
195
+
196
+        return $result;
197
+    }
198
+
199
+    /**
200
+     * Deletes the given folder by moving it into the trashbin.
201
+     *
202
+     * @param string $path path of folder to delete
203
+     *
204
+     * @return bool true if the operation succeeded, false otherwise
205
+     */
206
+    public function rmdir($path) {
207
+        if (isset(self::$moveOutOfSharedFolder[$this->mountPoint . $path])) {
208
+            $result = $this->doDelete($path, 'rmdir', true);
209
+            unset(self::$moveOutOfSharedFolder[$this->mountPoint . $path]);
210
+        } else {
211
+            $result = $this->doDelete($path, 'rmdir');
212
+        }
213
+
214
+        return $result;
215
+    }
216
+
217
+    /**
218
+     * check if it is a file located in data/user/files only files in the
219
+     * 'files' directory should be moved to the trash
220
+     *
221
+     * @param $path
222
+     * @return bool
223
+     */
224
+    protected function shouldMoveToTrash($path){
225
+
226
+        // check if there is a app which want to disable the trash bin for this file
227
+        $fileId = $this->storage->getCache()->getId($path);
228
+        $nodes = $this->rootFolder->getById($fileId);
229
+        foreach ($nodes as $node) {
230
+            $event = $this->createMoveToTrashEvent($node);
231
+            $this->eventDispatcher->dispatch('OCA\Files_Trashbin::moveToTrash', $event);
232
+            if ($event->shouldMoveToTrashBin() === false) {
233
+                return false;
234
+            }
235
+        }
236
+
237
+        $normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
238
+        $parts = explode('/', $normalized);
239
+        if (count($parts) < 4) {
240
+            return false;
241
+        }
242
+
243
+        if ($parts[2] === 'files' && $this->userManager->userExists($parts[1])) {
244
+            return true;
245
+        }
246
+
247
+        return false;
248
+    }
249
+
250
+    /**
251
+     * get move to trash event
252
+     *
253
+     * @param Node $node
254
+     * @return MoveToTrashEvent
255
+     */
256
+    protected function createMoveToTrashEvent(Node $node) {
257
+        return new MoveToTrashEvent($node);
258
+    }
259
+
260
+    /**
261
+     * Run the delete operation with the given method
262
+     *
263
+     * @param string $path path of file or folder to delete
264
+     * @param string $method either "unlink" or "rmdir"
265
+     * @param bool $ownerOnly delete for owner only (if file gets moved out of a shared folder)
266
+     *
267
+     * @return bool true if the operation succeeded, false otherwise
268
+     */
269
+    private function doDelete($path, $method, $ownerOnly = false) {
270
+        if (self::$disableTrash
271
+            || !\OC::$server->getAppManager()->isEnabledForUser('files_trashbin')
272
+            || (pathinfo($path, PATHINFO_EXTENSION) === 'part')
273
+            || $this->shouldMoveToTrash($path) === false
274
+        ) {
275
+            return call_user_func_array([$this->storage, $method], [$path]);
276
+        }
277
+
278
+        // check permissions before we continue, this is especially important for
279
+        // shared files
280
+        if (!$this->isDeletable($path)) {
281
+            return false;
282
+        }
283
+
284
+        $normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path, true, false, true);
285
+        $result = true;
286
+        $view = Filesystem::getView();
287
+        if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) {
288
+            $this->deletedFiles[$normalized] = $normalized;
289
+            if ($filesPath = $view->getRelativePath($normalized)) {
290
+                $filesPath = trim($filesPath, '/');
291
+                $result = \OCA\Files_Trashbin\Trashbin::move2trash($filesPath, $ownerOnly);
292
+                // in cross-storage cases the file will be copied
293
+                // but not deleted, so we delete it here
294
+                if ($result) {
295
+                    call_user_func_array([$this->storage, $method], [$path]);
296
+                }
297
+            } else {
298
+                $result = call_user_func_array([$this->storage, $method], [$path]);
299
+            }
300
+            unset($this->deletedFiles[$normalized]);
301
+        } else if ($this->storage->file_exists($path)) {
302
+            $result = call_user_func_array([$this->storage, $method], [$path]);
303
+        }
304
+
305
+        return $result;
306
+    }
307
+
308
+    /**
309
+     * Setup the storate wrapper callback
310
+     */
311
+    public static function setupStorage() {
312
+        \OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
313
+            return new \OCA\Files_Trashbin\Storage(
314
+                array('storage' => $storage, 'mountPoint' => $mountPoint),
315
+                \OC::$server->getUserManager(),
316
+                \OC::$server->getLogger(),
317
+                \OC::$server->getEventDispatcher(),
318
+                \OC::$server->getLazyRootFolder()
319
+            );
320
+        }, 1);
321
+    }
322 322
 
323 323
 }
Please login to merge, or discard this patch.