Completed
Push — master ( 3a44cc...20aa21 )
by Björn
19:48 queued 10s
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   +553 added lines, -553 removed lines patch added patch discarded remove patch
@@ -45,557 +45,557 @@
 block discarded – undo
45 45
 
46 46
 class Encryption implements IEncryptionModule {
47 47
 
48
-	const ID = 'OC_DEFAULT_MODULE';
49
-	const DISPLAY_NAME = 'Default encryption module';
50
-
51
-	/**
52
-	 * @var Crypt
53
-	 */
54
-	private $crypt;
55
-
56
-	/** @var string */
57
-	private $cipher;
58
-
59
-	/** @var string */
60
-	private $path;
61
-
62
-	/** @var string */
63
-	private $user;
64
-
65
-	/** @var  array */
66
-	private $owner;
67
-
68
-	/** @var string */
69
-	private $fileKey;
70
-
71
-	/** @var string */
72
-	private $writeCache;
73
-
74
-	/** @var KeyManager */
75
-	private $keyManager;
76
-
77
-	/** @var array */
78
-	private $accessList;
79
-
80
-	/** @var boolean */
81
-	private $isWriteOperation;
82
-
83
-	/** @var Util */
84
-	private $util;
85
-
86
-	/** @var  Session */
87
-	private $session;
88
-
89
-	/** @var  ILogger */
90
-	private $logger;
91
-
92
-	/** @var IL10N */
93
-	private $l;
94
-
95
-	/** @var EncryptAll */
96
-	private $encryptAll;
97
-
98
-	/** @var  bool */
99
-	private $useMasterPassword;
100
-
101
-	/** @var DecryptAll  */
102
-	private $decryptAll;
103
-
104
-	/** @var int unencrypted block size if block contains signature */
105
-	private $unencryptedBlockSizeSigned = 6072;
106
-
107
-	/** @var int unencrypted block size */
108
-	private $unencryptedBlockSize = 6126;
109
-
110
-	/** @var int Current version of the file */
111
-	private $version = 0;
112
-
113
-	/** @var array remember encryption signature version */
114
-	private static $rememberVersion = [];
115
-
116
-
117
-	/**
118
-	 *
119
-	 * @param Crypt $crypt
120
-	 * @param KeyManager $keyManager
121
-	 * @param Util $util
122
-	 * @param Session $session
123
-	 * @param EncryptAll $encryptAll
124
-	 * @param DecryptAll $decryptAll
125
-	 * @param ILogger $logger
126
-	 * @param IL10N $il10n
127
-	 */
128
-	public function __construct(Crypt $crypt,
129
-								KeyManager $keyManager,
130
-								Util $util,
131
-								Session $session,
132
-								EncryptAll $encryptAll,
133
-								DecryptAll $decryptAll,
134
-								ILogger $logger,
135
-								IL10N $il10n) {
136
-		$this->crypt = $crypt;
137
-		$this->keyManager = $keyManager;
138
-		$this->util = $util;
139
-		$this->session = $session;
140
-		$this->encryptAll = $encryptAll;
141
-		$this->decryptAll = $decryptAll;
142
-		$this->logger = $logger;
143
-		$this->l = $il10n;
144
-		$this->owner = [];
145
-		$this->useMasterPassword = $util->isMasterKeyEnabled();
146
-	}
147
-
148
-	/**
149
-	 * @return string defining the technical unique id
150
-	 */
151
-	public function getId() {
152
-		return self::ID;
153
-	}
154
-
155
-	/**
156
-	 * In comparison to getKey() this function returns a human readable (maybe translated) name
157
-	 *
158
-	 * @return string
159
-	 */
160
-	public function getDisplayName() {
161
-		return self::DISPLAY_NAME;
162
-	}
163
-
164
-	/**
165
-	 * start receiving chunks from a file. This is the place where you can
166
-	 * perform some initial step before starting encrypting/decrypting the
167
-	 * chunks
168
-	 *
169
-	 * @param string $path to the file
170
-	 * @param string $user who read/write the file
171
-	 * @param string $mode php stream open mode
172
-	 * @param array $header contains the header data read from the file
173
-	 * @param array $accessList who has access to the file contains the key 'users' and 'public'
174
-	 *
175
-	 * @return array $header contain data as key-value pairs which should be
176
-	 *                       written to the header, in case of a write operation
177
-	 *                       or if no additional data is needed return a empty array
178
-	 */
179
-	public function begin($path, $user, $mode, array $header, array $accessList) {
180
-		$this->path = $this->getPathToRealFile($path);
181
-		$this->accessList = $accessList;
182
-		$this->user = $user;
183
-		$this->isWriteOperation = false;
184
-		$this->writeCache = '';
185
-
186
-		if($this->session->isReady() === false) {
187
-			// if the master key is enabled we can initialize encryption
188
-			// with a empty password and user name
189
-			if ($this->util->isMasterKeyEnabled()) {
190
-				$this->keyManager->init('', '');
191
-			}
192
-		}
193
-
194
-		if ($this->session->decryptAllModeActivated()) {
195
-			$encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path);
196
-			$shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid());
197
-			$this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
198
-				$shareKey,
199
-				$this->session->getDecryptAllKey());
200
-		} else {
201
-			$this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
202
-		}
203
-
204
-		// always use the version from the original file, also part files
205
-		// need to have a correct version number if they get moved over to the
206
-		// final location
207
-		$this->version = (int)$this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
208
-
209
-		if (
210
-			$mode === 'w'
211
-			|| $mode === 'w+'
212
-			|| $mode === 'wb'
213
-			|| $mode === 'wb+'
214
-		) {
215
-			$this->isWriteOperation = true;
216
-			if (empty($this->fileKey)) {
217
-				$this->fileKey = $this->crypt->generateFileKey();
218
-			}
219
-		} else {
220
-			// if we read a part file we need to increase the version by 1
221
-			// because the version number was also increased by writing
222
-			// the part file
223
-			if(Scanner::isPartialFile($path)) {
224
-				$this->version = $this->version + 1;
225
-			}
226
-		}
227
-
228
-		if ($this->isWriteOperation) {
229
-			$this->cipher = $this->crypt->getCipher();
230
-		} elseif (isset($header['cipher'])) {
231
-			$this->cipher = $header['cipher'];
232
-		} else {
233
-			// if we read a file without a header we fall-back to the legacy cipher
234
-			// which was used in <=oC6
235
-			$this->cipher = $this->crypt->getLegacyCipher();
236
-		}
237
-
238
-		return array('cipher' => $this->cipher, 'signed' => 'true');
239
-	}
240
-
241
-	/**
242
-	 * last chunk received. This is the place where you can perform some final
243
-	 * operation and return some remaining data if something is left in your
244
-	 * buffer.
245
-	 *
246
-	 * @param string $path to the file
247
-	 * @param int $position
248
-	 * @return string remained data which should be written to the file in case
249
-	 *                of a write operation
250
-	 * @throws PublicKeyMissingException
251
-	 * @throws \Exception
252
-	 * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException
253
-	 */
254
-	public function end($path, $position = 0) {
255
-		$result = '';
256
-		if ($this->isWriteOperation) {
257
-			// in case of a part file we remember the new signature versions
258
-			// the version will be set later on update.
259
-			// This way we make sure that other apps listening to the pre-hooks
260
-			// still get the old version which should be the correct value for them
261
-			if (Scanner::isPartialFile($path)) {
262
-				self::$rememberVersion[$this->stripPartFileExtension($path)] = $this->version + 1;
263
-			}
264
-			if (!empty($this->writeCache)) {
265
-				$result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey, $this->version + 1, $position);
266
-				$this->writeCache = '';
267
-			}
268
-			$publicKeys = array();
269
-			if ($this->useMasterPassword === true) {
270
-				$publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
271
-			} else {
272
-				foreach ($this->accessList['users'] as $uid) {
273
-					try {
274
-						$publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
275
-					} catch (PublicKeyMissingException $e) {
276
-						$this->logger->warning(
277
-							'no public key found for user "{uid}", user will not be able to read the file',
278
-							['app' => 'encryption', 'uid' => $uid]
279
-						);
280
-						// if the public key of the owner is missing we should fail
281
-						if ($uid === $this->user) {
282
-							throw $e;
283
-						}
284
-					}
285
-				}
286
-			}
287
-
288
-			$publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys, $this->getOwner($path));
289
-			$encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys);
290
-			$this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles);
291
-		}
292
-		return $result;
293
-	}
294
-
295
-
296
-
297
-	/**
298
-	 * encrypt data
299
-	 *
300
-	 * @param string $data you want to encrypt
301
-	 * @param int $position
302
-	 * @return string encrypted data
303
-	 */
304
-	public function encrypt($data, $position = 0) {
305
-		// If extra data is left over from the last round, make sure it
306
-		// is integrated into the next block
307
-		if ($this->writeCache) {
308
-
309
-			// Concat writeCache to start of $data
310
-			$data = $this->writeCache . $data;
311
-
312
-			// Clear the write cache, ready for reuse - it has been
313
-			// flushed and its old contents processed
314
-			$this->writeCache = '';
315
-
316
-		}
317
-
318
-		$encrypted = '';
319
-		// While there still remains some data to be processed & written
320
-		while (strlen($data) > 0) {
321
-
322
-			// Remaining length for this iteration, not of the
323
-			// entire file (may be greater than 8192 bytes)
324
-			$remainingLength = strlen($data);
325
-
326
-			// If data remaining to be written is less than the
327
-			// size of 1 6126 byte block
328
-			if ($remainingLength < $this->unencryptedBlockSizeSigned) {
329
-
330
-				// Set writeCache to contents of $data
331
-				// The writeCache will be carried over to the
332
-				// next write round, and added to the start of
333
-				// $data to ensure that written blocks are
334
-				// always the correct length. If there is still
335
-				// data in writeCache after the writing round
336
-				// has finished, then the data will be written
337
-				// to disk by $this->flush().
338
-				$this->writeCache = $data;
339
-
340
-				// Clear $data ready for next round
341
-				$data = '';
342
-
343
-			} else {
344
-
345
-				// Read the chunk from the start of $data
346
-				$chunk = substr($data, 0, $this->unencryptedBlockSizeSigned);
347
-
348
-				$encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, $position);
349
-
350
-				// Remove the chunk we just processed from
351
-				// $data, leaving only unprocessed data in $data
352
-				// var, for handling on the next round
353
-				$data = substr($data, $this->unencryptedBlockSizeSigned);
354
-
355
-			}
356
-
357
-		}
358
-
359
-		return $encrypted;
360
-	}
361
-
362
-	/**
363
-	 * decrypt data
364
-	 *
365
-	 * @param string $data you want to decrypt
366
-	 * @param int $position
367
-	 * @return string decrypted data
368
-	 * @throws DecryptionFailedException
369
-	 */
370
-	public function decrypt($data, $position = 0) {
371
-		if (empty($this->fileKey)) {
372
-			$msg = 'Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.';
373
-			$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.');
374
-			$this->logger->error($msg);
375
-
376
-			throw new DecryptionFailedException($msg, $hint);
377
-		}
378
-
379
-		return $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher, $this->version, $position);
380
-	}
381
-
382
-	/**
383
-	 * update encrypted file, e.g. give additional users access to the file
384
-	 *
385
-	 * @param string $path path to the file which should be updated
386
-	 * @param string $uid of the user who performs the operation
387
-	 * @param array $accessList who has access to the file contains the key 'users' and 'public'
388
-	 * @return boolean
389
-	 */
390
-	public function update($path, $uid, array $accessList) {
391
-
392
-		if (empty($accessList)) {
393
-			if (isset(self::$rememberVersion[$path])) {
394
-				$this->keyManager->setVersion($path, self::$rememberVersion[$path], new View());
395
-				unset(self::$rememberVersion[$path]);
396
-			}
397
-			return;
398
-		}
399
-
400
-		$fileKey = $this->keyManager->getFileKey($path, $uid);
401
-
402
-		if (!empty($fileKey)) {
403
-
404
-			$publicKeys = array();
405
-			if ($this->useMasterPassword === true) {
406
-				$publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
407
-			} else {
408
-				foreach ($accessList['users'] as $user) {
409
-					try {
410
-						$publicKeys[$user] = $this->keyManager->getPublicKey($user);
411
-					} catch (PublicKeyMissingException $e) {
412
-						$this->logger->warning('Could not encrypt file for ' . $user . ': ' . $e->getMessage());
413
-					}
414
-				}
415
-			}
416
-
417
-			$publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->getOwner($path));
418
-
419
-			$encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
420
-
421
-			$this->keyManager->deleteAllFileKeys($path);
422
-
423
-			$this->keyManager->setAllFileKeys($path, $encryptedFileKey);
424
-
425
-		} else {
426
-			$this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted',
427
-				array('file' => $path, 'app' => 'encryption'));
428
-
429
-			return false;
430
-		}
431
-
432
-		return true;
433
-	}
434
-
435
-	/**
436
-	 * should the file be encrypted or not
437
-	 *
438
-	 * @param string $path
439
-	 * @return boolean
440
-	 */
441
-	public function shouldEncrypt($path) {
442
-		if ($this->util->shouldEncryptHomeStorage() === false) {
443
-			$storage = $this->util->getStorage($path);
444
-			if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
445
-				return false;
446
-			}
447
-		}
448
-		$parts = explode('/', $path);
449
-		if (count($parts) < 4) {
450
-			return false;
451
-		}
452
-
453
-		if ($parts[2] === 'files') {
454
-			return true;
455
-		}
456
-		if ($parts[2] === 'files_versions') {
457
-			return true;
458
-		}
459
-		if ($parts[2] === 'files_trashbin') {
460
-			return true;
461
-		}
462
-
463
-		return false;
464
-	}
465
-
466
-	/**
467
-	 * get size of the unencrypted payload per block.
468
-	 * Nextcloud read/write files with a block size of 8192 byte
469
-	 *
470
-	 * @param bool $signed
471
-	 * @return int
472
-	 */
473
-	public function getUnencryptedBlockSize($signed = false) {
474
-		if ($signed === false) {
475
-			return $this->unencryptedBlockSize;
476
-		}
477
-
478
-		return $this->unencryptedBlockSizeSigned;
479
-	}
480
-
481
-	/**
482
-	 * check if the encryption module is able to read the file,
483
-	 * e.g. if all encryption keys exists
484
-	 *
485
-	 * @param string $path
486
-	 * @param string $uid user for whom we want to check if he can read the file
487
-	 * @return bool
488
-	 * @throws DecryptionFailedException
489
-	 */
490
-	public function isReadable($path, $uid) {
491
-		$fileKey = $this->keyManager->getFileKey($path, $uid);
492
-		if (empty($fileKey)) {
493
-			$owner = $this->util->getOwner($path);
494
-			if ($owner !== $uid) {
495
-				// if it is a shared file we throw a exception with a useful
496
-				// error message because in this case it means that the file was
497
-				// shared with the user at a point where the user didn't had a
498
-				// valid private/public key
499
-				$msg = 'Encryption module "' . $this->getDisplayName() .
500
-					'" is not able to read ' . $path;
501
-				$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.');
502
-				$this->logger->warning($msg);
503
-				throw new DecryptionFailedException($msg, $hint);
504
-			}
505
-			return false;
506
-		}
507
-
508
-		return true;
509
-	}
510
-
511
-	/**
512
-	 * Initial encryption of all files
513
-	 *
514
-	 * @param InputInterface $input
515
-	 * @param OutputInterface $output write some status information to the terminal during encryption
516
-	 */
517
-	public function encryptAll(InputInterface $input, OutputInterface $output) {
518
-		$this->encryptAll->encryptAll($input, $output);
519
-	}
520
-
521
-	/**
522
-	 * prepare module to perform decrypt all operation
523
-	 *
524
-	 * @param InputInterface $input
525
-	 * @param OutputInterface $output
526
-	 * @param string $user
527
-	 * @return bool
528
-	 */
529
-	public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '') {
530
-		return $this->decryptAll->prepare($input, $output, $user);
531
-	}
532
-
533
-
534
-	/**
535
-	 * @param string $path
536
-	 * @return string
537
-	 */
538
-	protected function getPathToRealFile($path) {
539
-		$realPath = $path;
540
-		$parts = explode('/', $path);
541
-		if ($parts[2] === 'files_versions') {
542
-			$realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
543
-			$length = strrpos($realPath, '.');
544
-			$realPath = substr($realPath, 0, $length);
545
-		}
546
-
547
-		return $realPath;
548
-	}
549
-
550
-	/**
551
-	 * remove .part file extension and the ocTransferId from the file to get the
552
-	 * original file name
553
-	 *
554
-	 * @param string $path
555
-	 * @return string
556
-	 */
557
-	protected function stripPartFileExtension($path) {
558
-		if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
559
-			$pos = strrpos($path, '.', -6);
560
-			$path = substr($path, 0, $pos);
561
-		}
562
-
563
-		return $path;
564
-	}
565
-
566
-	/**
567
-	 * get owner of a file
568
-	 *
569
-	 * @param string $path
570
-	 * @return string
571
-	 */
572
-	protected function getOwner($path) {
573
-		if (!isset($this->owner[$path])) {
574
-			$this->owner[$path] = $this->util->getOwner($path);
575
-		}
576
-		return $this->owner[$path];
577
-	}
578
-
579
-	/**
580
-	 * Check if the module is ready to be used by that specific user.
581
-	 * In case a module is not ready - because e.g. key pairs have not been generated
582
-	 * upon login this method can return false before any operation starts and might
583
-	 * cause issues during operations.
584
-	 *
585
-	 * @param string $user
586
-	 * @return boolean
587
-	 * @since 9.1.0
588
-	 */
589
-	public function isReadyForUser($user) {
590
-		return $this->keyManager->userHasKeys($user);
591
-	}
592
-
593
-	/**
594
-	 * We only need a detailed access list if the master key is not enabled
595
-	 *
596
-	 * @return bool
597
-	 */
598
-	public function needDetailedAccessList() {
599
-		return !$this->util->isMasterKeyEnabled();
600
-	}
48
+    const ID = 'OC_DEFAULT_MODULE';
49
+    const DISPLAY_NAME = 'Default encryption module';
50
+
51
+    /**
52
+     * @var Crypt
53
+     */
54
+    private $crypt;
55
+
56
+    /** @var string */
57
+    private $cipher;
58
+
59
+    /** @var string */
60
+    private $path;
61
+
62
+    /** @var string */
63
+    private $user;
64
+
65
+    /** @var  array */
66
+    private $owner;
67
+
68
+    /** @var string */
69
+    private $fileKey;
70
+
71
+    /** @var string */
72
+    private $writeCache;
73
+
74
+    /** @var KeyManager */
75
+    private $keyManager;
76
+
77
+    /** @var array */
78
+    private $accessList;
79
+
80
+    /** @var boolean */
81
+    private $isWriteOperation;
82
+
83
+    /** @var Util */
84
+    private $util;
85
+
86
+    /** @var  Session */
87
+    private $session;
88
+
89
+    /** @var  ILogger */
90
+    private $logger;
91
+
92
+    /** @var IL10N */
93
+    private $l;
94
+
95
+    /** @var EncryptAll */
96
+    private $encryptAll;
97
+
98
+    /** @var  bool */
99
+    private $useMasterPassword;
100
+
101
+    /** @var DecryptAll  */
102
+    private $decryptAll;
103
+
104
+    /** @var int unencrypted block size if block contains signature */
105
+    private $unencryptedBlockSizeSigned = 6072;
106
+
107
+    /** @var int unencrypted block size */
108
+    private $unencryptedBlockSize = 6126;
109
+
110
+    /** @var int Current version of the file */
111
+    private $version = 0;
112
+
113
+    /** @var array remember encryption signature version */
114
+    private static $rememberVersion = [];
115
+
116
+
117
+    /**
118
+     *
119
+     * @param Crypt $crypt
120
+     * @param KeyManager $keyManager
121
+     * @param Util $util
122
+     * @param Session $session
123
+     * @param EncryptAll $encryptAll
124
+     * @param DecryptAll $decryptAll
125
+     * @param ILogger $logger
126
+     * @param IL10N $il10n
127
+     */
128
+    public function __construct(Crypt $crypt,
129
+                                KeyManager $keyManager,
130
+                                Util $util,
131
+                                Session $session,
132
+                                EncryptAll $encryptAll,
133
+                                DecryptAll $decryptAll,
134
+                                ILogger $logger,
135
+                                IL10N $il10n) {
136
+        $this->crypt = $crypt;
137
+        $this->keyManager = $keyManager;
138
+        $this->util = $util;
139
+        $this->session = $session;
140
+        $this->encryptAll = $encryptAll;
141
+        $this->decryptAll = $decryptAll;
142
+        $this->logger = $logger;
143
+        $this->l = $il10n;
144
+        $this->owner = [];
145
+        $this->useMasterPassword = $util->isMasterKeyEnabled();
146
+    }
147
+
148
+    /**
149
+     * @return string defining the technical unique id
150
+     */
151
+    public function getId() {
152
+        return self::ID;
153
+    }
154
+
155
+    /**
156
+     * In comparison to getKey() this function returns a human readable (maybe translated) name
157
+     *
158
+     * @return string
159
+     */
160
+    public function getDisplayName() {
161
+        return self::DISPLAY_NAME;
162
+    }
163
+
164
+    /**
165
+     * start receiving chunks from a file. This is the place where you can
166
+     * perform some initial step before starting encrypting/decrypting the
167
+     * chunks
168
+     *
169
+     * @param string $path to the file
170
+     * @param string $user who read/write the file
171
+     * @param string $mode php stream open mode
172
+     * @param array $header contains the header data read from the file
173
+     * @param array $accessList who has access to the file contains the key 'users' and 'public'
174
+     *
175
+     * @return array $header contain data as key-value pairs which should be
176
+     *                       written to the header, in case of a write operation
177
+     *                       or if no additional data is needed return a empty array
178
+     */
179
+    public function begin($path, $user, $mode, array $header, array $accessList) {
180
+        $this->path = $this->getPathToRealFile($path);
181
+        $this->accessList = $accessList;
182
+        $this->user = $user;
183
+        $this->isWriteOperation = false;
184
+        $this->writeCache = '';
185
+
186
+        if($this->session->isReady() === false) {
187
+            // if the master key is enabled we can initialize encryption
188
+            // with a empty password and user name
189
+            if ($this->util->isMasterKeyEnabled()) {
190
+                $this->keyManager->init('', '');
191
+            }
192
+        }
193
+
194
+        if ($this->session->decryptAllModeActivated()) {
195
+            $encryptedFileKey = $this->keyManager->getEncryptedFileKey($this->path);
196
+            $shareKey = $this->keyManager->getShareKey($this->path, $this->session->getDecryptAllUid());
197
+            $this->fileKey = $this->crypt->multiKeyDecrypt($encryptedFileKey,
198
+                $shareKey,
199
+                $this->session->getDecryptAllKey());
200
+        } else {
201
+            $this->fileKey = $this->keyManager->getFileKey($this->path, $this->user);
202
+        }
203
+
204
+        // always use the version from the original file, also part files
205
+        // need to have a correct version number if they get moved over to the
206
+        // final location
207
+        $this->version = (int)$this->keyManager->getVersion($this->stripPartFileExtension($path), new View());
208
+
209
+        if (
210
+            $mode === 'w'
211
+            || $mode === 'w+'
212
+            || $mode === 'wb'
213
+            || $mode === 'wb+'
214
+        ) {
215
+            $this->isWriteOperation = true;
216
+            if (empty($this->fileKey)) {
217
+                $this->fileKey = $this->crypt->generateFileKey();
218
+            }
219
+        } else {
220
+            // if we read a part file we need to increase the version by 1
221
+            // because the version number was also increased by writing
222
+            // the part file
223
+            if(Scanner::isPartialFile($path)) {
224
+                $this->version = $this->version + 1;
225
+            }
226
+        }
227
+
228
+        if ($this->isWriteOperation) {
229
+            $this->cipher = $this->crypt->getCipher();
230
+        } elseif (isset($header['cipher'])) {
231
+            $this->cipher = $header['cipher'];
232
+        } else {
233
+            // if we read a file without a header we fall-back to the legacy cipher
234
+            // which was used in <=oC6
235
+            $this->cipher = $this->crypt->getLegacyCipher();
236
+        }
237
+
238
+        return array('cipher' => $this->cipher, 'signed' => 'true');
239
+    }
240
+
241
+    /**
242
+     * last chunk received. This is the place where you can perform some final
243
+     * operation and return some remaining data if something is left in your
244
+     * buffer.
245
+     *
246
+     * @param string $path to the file
247
+     * @param int $position
248
+     * @return string remained data which should be written to the file in case
249
+     *                of a write operation
250
+     * @throws PublicKeyMissingException
251
+     * @throws \Exception
252
+     * @throws \OCA\Encryption\Exceptions\MultiKeyEncryptException
253
+     */
254
+    public function end($path, $position = 0) {
255
+        $result = '';
256
+        if ($this->isWriteOperation) {
257
+            // in case of a part file we remember the new signature versions
258
+            // the version will be set later on update.
259
+            // This way we make sure that other apps listening to the pre-hooks
260
+            // still get the old version which should be the correct value for them
261
+            if (Scanner::isPartialFile($path)) {
262
+                self::$rememberVersion[$this->stripPartFileExtension($path)] = $this->version + 1;
263
+            }
264
+            if (!empty($this->writeCache)) {
265
+                $result = $this->crypt->symmetricEncryptFileContent($this->writeCache, $this->fileKey, $this->version + 1, $position);
266
+                $this->writeCache = '';
267
+            }
268
+            $publicKeys = array();
269
+            if ($this->useMasterPassword === true) {
270
+                $publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
271
+            } else {
272
+                foreach ($this->accessList['users'] as $uid) {
273
+                    try {
274
+                        $publicKeys[$uid] = $this->keyManager->getPublicKey($uid);
275
+                    } catch (PublicKeyMissingException $e) {
276
+                        $this->logger->warning(
277
+                            'no public key found for user "{uid}", user will not be able to read the file',
278
+                            ['app' => 'encryption', 'uid' => $uid]
279
+                        );
280
+                        // if the public key of the owner is missing we should fail
281
+                        if ($uid === $this->user) {
282
+                            throw $e;
283
+                        }
284
+                    }
285
+                }
286
+            }
287
+
288
+            $publicKeys = $this->keyManager->addSystemKeys($this->accessList, $publicKeys, $this->getOwner($path));
289
+            $encryptedKeyfiles = $this->crypt->multiKeyEncrypt($this->fileKey, $publicKeys);
290
+            $this->keyManager->setAllFileKeys($this->path, $encryptedKeyfiles);
291
+        }
292
+        return $result;
293
+    }
294
+
295
+
296
+
297
+    /**
298
+     * encrypt data
299
+     *
300
+     * @param string $data you want to encrypt
301
+     * @param int $position
302
+     * @return string encrypted data
303
+     */
304
+    public function encrypt($data, $position = 0) {
305
+        // If extra data is left over from the last round, make sure it
306
+        // is integrated into the next block
307
+        if ($this->writeCache) {
308
+
309
+            // Concat writeCache to start of $data
310
+            $data = $this->writeCache . $data;
311
+
312
+            // Clear the write cache, ready for reuse - it has been
313
+            // flushed and its old contents processed
314
+            $this->writeCache = '';
315
+
316
+        }
317
+
318
+        $encrypted = '';
319
+        // While there still remains some data to be processed & written
320
+        while (strlen($data) > 0) {
321
+
322
+            // Remaining length for this iteration, not of the
323
+            // entire file (may be greater than 8192 bytes)
324
+            $remainingLength = strlen($data);
325
+
326
+            // If data remaining to be written is less than the
327
+            // size of 1 6126 byte block
328
+            if ($remainingLength < $this->unencryptedBlockSizeSigned) {
329
+
330
+                // Set writeCache to contents of $data
331
+                // The writeCache will be carried over to the
332
+                // next write round, and added to the start of
333
+                // $data to ensure that written blocks are
334
+                // always the correct length. If there is still
335
+                // data in writeCache after the writing round
336
+                // has finished, then the data will be written
337
+                // to disk by $this->flush().
338
+                $this->writeCache = $data;
339
+
340
+                // Clear $data ready for next round
341
+                $data = '';
342
+
343
+            } else {
344
+
345
+                // Read the chunk from the start of $data
346
+                $chunk = substr($data, 0, $this->unencryptedBlockSizeSigned);
347
+
348
+                $encrypted .= $this->crypt->symmetricEncryptFileContent($chunk, $this->fileKey, $this->version + 1, $position);
349
+
350
+                // Remove the chunk we just processed from
351
+                // $data, leaving only unprocessed data in $data
352
+                // var, for handling on the next round
353
+                $data = substr($data, $this->unencryptedBlockSizeSigned);
354
+
355
+            }
356
+
357
+        }
358
+
359
+        return $encrypted;
360
+    }
361
+
362
+    /**
363
+     * decrypt data
364
+     *
365
+     * @param string $data you want to decrypt
366
+     * @param int $position
367
+     * @return string decrypted data
368
+     * @throws DecryptionFailedException
369
+     */
370
+    public function decrypt($data, $position = 0) {
371
+        if (empty($this->fileKey)) {
372
+            $msg = 'Can not decrypt this file, probably this is a shared file. Please ask the file owner to reshare the file with you.';
373
+            $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.');
374
+            $this->logger->error($msg);
375
+
376
+            throw new DecryptionFailedException($msg, $hint);
377
+        }
378
+
379
+        return $this->crypt->symmetricDecryptFileContent($data, $this->fileKey, $this->cipher, $this->version, $position);
380
+    }
381
+
382
+    /**
383
+     * update encrypted file, e.g. give additional users access to the file
384
+     *
385
+     * @param string $path path to the file which should be updated
386
+     * @param string $uid of the user who performs the operation
387
+     * @param array $accessList who has access to the file contains the key 'users' and 'public'
388
+     * @return boolean
389
+     */
390
+    public function update($path, $uid, array $accessList) {
391
+
392
+        if (empty($accessList)) {
393
+            if (isset(self::$rememberVersion[$path])) {
394
+                $this->keyManager->setVersion($path, self::$rememberVersion[$path], new View());
395
+                unset(self::$rememberVersion[$path]);
396
+            }
397
+            return;
398
+        }
399
+
400
+        $fileKey = $this->keyManager->getFileKey($path, $uid);
401
+
402
+        if (!empty($fileKey)) {
403
+
404
+            $publicKeys = array();
405
+            if ($this->useMasterPassword === true) {
406
+                $publicKeys[$this->keyManager->getMasterKeyId()] = $this->keyManager->getPublicMasterKey();
407
+            } else {
408
+                foreach ($accessList['users'] as $user) {
409
+                    try {
410
+                        $publicKeys[$user] = $this->keyManager->getPublicKey($user);
411
+                    } catch (PublicKeyMissingException $e) {
412
+                        $this->logger->warning('Could not encrypt file for ' . $user . ': ' . $e->getMessage());
413
+                    }
414
+                }
415
+            }
416
+
417
+            $publicKeys = $this->keyManager->addSystemKeys($accessList, $publicKeys, $this->getOwner($path));
418
+
419
+            $encryptedFileKey = $this->crypt->multiKeyEncrypt($fileKey, $publicKeys);
420
+
421
+            $this->keyManager->deleteAllFileKeys($path);
422
+
423
+            $this->keyManager->setAllFileKeys($path, $encryptedFileKey);
424
+
425
+        } else {
426
+            $this->logger->debug('no file key found, we assume that the file "{file}" is not encrypted',
427
+                array('file' => $path, 'app' => 'encryption'));
428
+
429
+            return false;
430
+        }
431
+
432
+        return true;
433
+    }
434
+
435
+    /**
436
+     * should the file be encrypted or not
437
+     *
438
+     * @param string $path
439
+     * @return boolean
440
+     */
441
+    public function shouldEncrypt($path) {
442
+        if ($this->util->shouldEncryptHomeStorage() === false) {
443
+            $storage = $this->util->getStorage($path);
444
+            if ($storage->instanceOfStorage('\OCP\Files\IHomeStorage')) {
445
+                return false;
446
+            }
447
+        }
448
+        $parts = explode('/', $path);
449
+        if (count($parts) < 4) {
450
+            return false;
451
+        }
452
+
453
+        if ($parts[2] === 'files') {
454
+            return true;
455
+        }
456
+        if ($parts[2] === 'files_versions') {
457
+            return true;
458
+        }
459
+        if ($parts[2] === 'files_trashbin') {
460
+            return true;
461
+        }
462
+
463
+        return false;
464
+    }
465
+
466
+    /**
467
+     * get size of the unencrypted payload per block.
468
+     * Nextcloud read/write files with a block size of 8192 byte
469
+     *
470
+     * @param bool $signed
471
+     * @return int
472
+     */
473
+    public function getUnencryptedBlockSize($signed = false) {
474
+        if ($signed === false) {
475
+            return $this->unencryptedBlockSize;
476
+        }
477
+
478
+        return $this->unencryptedBlockSizeSigned;
479
+    }
480
+
481
+    /**
482
+     * check if the encryption module is able to read the file,
483
+     * e.g. if all encryption keys exists
484
+     *
485
+     * @param string $path
486
+     * @param string $uid user for whom we want to check if he can read the file
487
+     * @return bool
488
+     * @throws DecryptionFailedException
489
+     */
490
+    public function isReadable($path, $uid) {
491
+        $fileKey = $this->keyManager->getFileKey($path, $uid);
492
+        if (empty($fileKey)) {
493
+            $owner = $this->util->getOwner($path);
494
+            if ($owner !== $uid) {
495
+                // if it is a shared file we throw a exception with a useful
496
+                // error message because in this case it means that the file was
497
+                // shared with the user at a point where the user didn't had a
498
+                // valid private/public key
499
+                $msg = 'Encryption module "' . $this->getDisplayName() .
500
+                    '" is not able to read ' . $path;
501
+                $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.');
502
+                $this->logger->warning($msg);
503
+                throw new DecryptionFailedException($msg, $hint);
504
+            }
505
+            return false;
506
+        }
507
+
508
+        return true;
509
+    }
510
+
511
+    /**
512
+     * Initial encryption of all files
513
+     *
514
+     * @param InputInterface $input
515
+     * @param OutputInterface $output write some status information to the terminal during encryption
516
+     */
517
+    public function encryptAll(InputInterface $input, OutputInterface $output) {
518
+        $this->encryptAll->encryptAll($input, $output);
519
+    }
520
+
521
+    /**
522
+     * prepare module to perform decrypt all operation
523
+     *
524
+     * @param InputInterface $input
525
+     * @param OutputInterface $output
526
+     * @param string $user
527
+     * @return bool
528
+     */
529
+    public function prepareDecryptAll(InputInterface $input, OutputInterface $output, $user = '') {
530
+        return $this->decryptAll->prepare($input, $output, $user);
531
+    }
532
+
533
+
534
+    /**
535
+     * @param string $path
536
+     * @return string
537
+     */
538
+    protected function getPathToRealFile($path) {
539
+        $realPath = $path;
540
+        $parts = explode('/', $path);
541
+        if ($parts[2] === 'files_versions') {
542
+            $realPath = '/' . $parts[1] . '/files/' . implode('/', array_slice($parts, 3));
543
+            $length = strrpos($realPath, '.');
544
+            $realPath = substr($realPath, 0, $length);
545
+        }
546
+
547
+        return $realPath;
548
+    }
549
+
550
+    /**
551
+     * remove .part file extension and the ocTransferId from the file to get the
552
+     * original file name
553
+     *
554
+     * @param string $path
555
+     * @return string
556
+     */
557
+    protected function stripPartFileExtension($path) {
558
+        if (pathinfo($path, PATHINFO_EXTENSION) === 'part') {
559
+            $pos = strrpos($path, '.', -6);
560
+            $path = substr($path, 0, $pos);
561
+        }
562
+
563
+        return $path;
564
+    }
565
+
566
+    /**
567
+     * get owner of a file
568
+     *
569
+     * @param string $path
570
+     * @return string
571
+     */
572
+    protected function getOwner($path) {
573
+        if (!isset($this->owner[$path])) {
574
+            $this->owner[$path] = $this->util->getOwner($path);
575
+        }
576
+        return $this->owner[$path];
577
+    }
578
+
579
+    /**
580
+     * Check if the module is ready to be used by that specific user.
581
+     * In case a module is not ready - because e.g. key pairs have not been generated
582
+     * upon login this method can return false before any operation starts and might
583
+     * cause issues during operations.
584
+     *
585
+     * @param string $user
586
+     * @return boolean
587
+     * @since 9.1.0
588
+     */
589
+    public function isReadyForUser($user) {
590
+        return $this->keyManager->userHasKeys($user);
591
+    }
592
+
593
+    /**
594
+     * We only need a detailed access list if the master key is not enabled
595
+     *
596
+     * @return bool
597
+     */
598
+    public function needDetailedAccessList() {
599
+        return !$this->util->isMasterKeyEnabled();
600
+    }
601 601
 }
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.
Indentation   +1030 added lines, -1030 removed lines patch added patch discarded remove patch
@@ -55,1044 +55,1044 @@
 block discarded – undo
55 55
  */
56 56
 class FederatedShareProvider implements IShareProvider {
57 57
 
58
-	const SHARE_TYPE_REMOTE = 6;
59
-
60
-	/** @var IDBConnection */
61
-	private $dbConnection;
62
-
63
-	/** @var AddressHandler */
64
-	private $addressHandler;
65
-
66
-	/** @var Notifications */
67
-	private $notifications;
68
-
69
-	/** @var TokenHandler */
70
-	private $tokenHandler;
71
-
72
-	/** @var IL10N */
73
-	private $l;
74
-
75
-	/** @var ILogger */
76
-	private $logger;
77
-
78
-	/** @var IRootFolder */
79
-	private $rootFolder;
80
-
81
-	/** @var IConfig */
82
-	private $config;
83
-
84
-	/** @var string */
85
-	private $externalShareTable = 'share_external';
86
-
87
-	/** @var IUserManager */
88
-	private $userManager;
89
-
90
-	/** @var ICloudIdManager */
91
-	private $cloudIdManager;
92
-
93
-	/** @var \OCP\GlobalScale\IConfig */
94
-	private $gsConfig;
95
-
96
-	/** @var ICloudFederationProviderManager */
97
-	private $cloudFederationProviderManager;
98
-
99
-	/** @var array list of supported share types */
100
-	private $supportedShareType = [\OCP\Share::SHARE_TYPE_REMOTE_GROUP, \OCP\Share::SHARE_TYPE_REMOTE];
101
-
102
-	/**
103
-	 * DefaultShareProvider constructor.
104
-	 *
105
-	 * @param IDBConnection $connection
106
-	 * @param AddressHandler $addressHandler
107
-	 * @param Notifications $notifications
108
-	 * @param TokenHandler $tokenHandler
109
-	 * @param IL10N $l10n
110
-	 * @param ILogger $logger
111
-	 * @param IRootFolder $rootFolder
112
-	 * @param IConfig $config
113
-	 * @param IUserManager $userManager
114
-	 * @param ICloudIdManager $cloudIdManager
115
-	 * @param \OCP\GlobalScale\IConfig $globalScaleConfig
116
-	 * @param ICloudFederationProviderManager $cloudFederationProviderManager
117
-	 */
118
-	public function __construct(
119
-			IDBConnection $connection,
120
-			AddressHandler $addressHandler,
121
-			Notifications $notifications,
122
-			TokenHandler $tokenHandler,
123
-			IL10N $l10n,
124
-			ILogger $logger,
125
-			IRootFolder $rootFolder,
126
-			IConfig $config,
127
-			IUserManager $userManager,
128
-			ICloudIdManager $cloudIdManager,
129
-			\OCP\GlobalScale\IConfig $globalScaleConfig,
130
-			ICloudFederationProviderManager $cloudFederationProviderManager
131
-	) {
132
-		$this->dbConnection = $connection;
133
-		$this->addressHandler = $addressHandler;
134
-		$this->notifications = $notifications;
135
-		$this->tokenHandler = $tokenHandler;
136
-		$this->l = $l10n;
137
-		$this->logger = $logger;
138
-		$this->rootFolder = $rootFolder;
139
-		$this->config = $config;
140
-		$this->userManager = $userManager;
141
-		$this->cloudIdManager = $cloudIdManager;
142
-		$this->gsConfig = $globalScaleConfig;
143
-		$this->cloudFederationProviderManager = $cloudFederationProviderManager;
144
-	}
145
-
146
-	/**
147
-	 * Return the identifier of this provider.
148
-	 *
149
-	 * @return string Containing only [a-zA-Z0-9]
150
-	 */
151
-	public function identifier() {
152
-		return 'ocFederatedSharing';
153
-	}
154
-
155
-	/**
156
-	 * Share a path
157
-	 *
158
-	 * @param IShare $share
159
-	 * @return IShare The share object
160
-	 * @throws ShareNotFound
161
-	 * @throws \Exception
162
-	 */
163
-	public function create(IShare $share) {
164
-
165
-		$shareWith = $share->getSharedWith();
166
-		$itemSource = $share->getNodeId();
167
-		$itemType = $share->getNodeType();
168
-		$permissions = $share->getPermissions();
169
-		$sharedBy = $share->getSharedBy();
170
-		$shareType = $share->getShareType();
171
-
172
-		if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE_GROUP &&
173
-			!$this->isOutgoingServer2serverGroupShareEnabled()
174
-		) {
175
-			$message = 'It is not allowed to send federated group shares from this server.';
176
-			$message_t = $this->l->t('It is not allowed to send federated group shares from this server.');
177
-			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
178
-			throw new \Exception($message_t);
179
-		}
180
-
181
-		/*
58
+    const SHARE_TYPE_REMOTE = 6;
59
+
60
+    /** @var IDBConnection */
61
+    private $dbConnection;
62
+
63
+    /** @var AddressHandler */
64
+    private $addressHandler;
65
+
66
+    /** @var Notifications */
67
+    private $notifications;
68
+
69
+    /** @var TokenHandler */
70
+    private $tokenHandler;
71
+
72
+    /** @var IL10N */
73
+    private $l;
74
+
75
+    /** @var ILogger */
76
+    private $logger;
77
+
78
+    /** @var IRootFolder */
79
+    private $rootFolder;
80
+
81
+    /** @var IConfig */
82
+    private $config;
83
+
84
+    /** @var string */
85
+    private $externalShareTable = 'share_external';
86
+
87
+    /** @var IUserManager */
88
+    private $userManager;
89
+
90
+    /** @var ICloudIdManager */
91
+    private $cloudIdManager;
92
+
93
+    /** @var \OCP\GlobalScale\IConfig */
94
+    private $gsConfig;
95
+
96
+    /** @var ICloudFederationProviderManager */
97
+    private $cloudFederationProviderManager;
98
+
99
+    /** @var array list of supported share types */
100
+    private $supportedShareType = [\OCP\Share::SHARE_TYPE_REMOTE_GROUP, \OCP\Share::SHARE_TYPE_REMOTE];
101
+
102
+    /**
103
+     * DefaultShareProvider constructor.
104
+     *
105
+     * @param IDBConnection $connection
106
+     * @param AddressHandler $addressHandler
107
+     * @param Notifications $notifications
108
+     * @param TokenHandler $tokenHandler
109
+     * @param IL10N $l10n
110
+     * @param ILogger $logger
111
+     * @param IRootFolder $rootFolder
112
+     * @param IConfig $config
113
+     * @param IUserManager $userManager
114
+     * @param ICloudIdManager $cloudIdManager
115
+     * @param \OCP\GlobalScale\IConfig $globalScaleConfig
116
+     * @param ICloudFederationProviderManager $cloudFederationProviderManager
117
+     */
118
+    public function __construct(
119
+            IDBConnection $connection,
120
+            AddressHandler $addressHandler,
121
+            Notifications $notifications,
122
+            TokenHandler $tokenHandler,
123
+            IL10N $l10n,
124
+            ILogger $logger,
125
+            IRootFolder $rootFolder,
126
+            IConfig $config,
127
+            IUserManager $userManager,
128
+            ICloudIdManager $cloudIdManager,
129
+            \OCP\GlobalScale\IConfig $globalScaleConfig,
130
+            ICloudFederationProviderManager $cloudFederationProviderManager
131
+    ) {
132
+        $this->dbConnection = $connection;
133
+        $this->addressHandler = $addressHandler;
134
+        $this->notifications = $notifications;
135
+        $this->tokenHandler = $tokenHandler;
136
+        $this->l = $l10n;
137
+        $this->logger = $logger;
138
+        $this->rootFolder = $rootFolder;
139
+        $this->config = $config;
140
+        $this->userManager = $userManager;
141
+        $this->cloudIdManager = $cloudIdManager;
142
+        $this->gsConfig = $globalScaleConfig;
143
+        $this->cloudFederationProviderManager = $cloudFederationProviderManager;
144
+    }
145
+
146
+    /**
147
+     * Return the identifier of this provider.
148
+     *
149
+     * @return string Containing only [a-zA-Z0-9]
150
+     */
151
+    public function identifier() {
152
+        return 'ocFederatedSharing';
153
+    }
154
+
155
+    /**
156
+     * Share a path
157
+     *
158
+     * @param IShare $share
159
+     * @return IShare The share object
160
+     * @throws ShareNotFound
161
+     * @throws \Exception
162
+     */
163
+    public function create(IShare $share) {
164
+
165
+        $shareWith = $share->getSharedWith();
166
+        $itemSource = $share->getNodeId();
167
+        $itemType = $share->getNodeType();
168
+        $permissions = $share->getPermissions();
169
+        $sharedBy = $share->getSharedBy();
170
+        $shareType = $share->getShareType();
171
+
172
+        if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE_GROUP &&
173
+            !$this->isOutgoingServer2serverGroupShareEnabled()
174
+        ) {
175
+            $message = 'It is not allowed to send federated group shares from this server.';
176
+            $message_t = $this->l->t('It is not allowed to send federated group shares from this server.');
177
+            $this->logger->debug($message, ['app' => 'Federated File Sharing']);
178
+            throw new \Exception($message_t);
179
+        }
180
+
181
+        /*
182 182
 		 * Check if file is not already shared with the remote user
183 183
 		 */
184
-		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
185
-		$alreadySharedGroup = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE_GROUP, $share->getNode(), 1, 0);
186
-		if (!empty($alreadyShared) || !empty($alreadySharedGroup)) {
187
-			$message = 'Sharing %s failed, because this item is already shared with %s';
188
-			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
189
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
190
-			throw new \Exception($message_t);
191
-		}
192
-
193
-
194
-		// don't allow federated shares if source and target server are the same
195
-		$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
196
-		$currentServer = $this->addressHandler->generateRemoteURL();
197
-		$currentUser = $sharedBy;
198
-		if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
199
-			$message = 'Not allowed to create a federated share with the same user.';
200
-			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
201
-			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
202
-			throw new \Exception($message_t);
203
-		}
204
-
205
-
206
-		$share->setSharedWith($cloudId->getId());
207
-
208
-		try {
209
-			$remoteShare = $this->getShareFromExternalShareTable($share);
210
-		} catch (ShareNotFound $e) {
211
-			$remoteShare = null;
212
-		}
213
-
214
-		if ($remoteShare) {
215
-			try {
216
-				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
217
-				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType);
218
-				$share->setId($shareId);
219
-				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
220
-				// remote share was create successfully if we get a valid token as return
221
-				$send = is_string($token) && $token !== '';
222
-			} catch (\Exception $e) {
223
-				// fall back to old re-share behavior if the remote server
224
-				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
225
-				$this->removeShareFromTable($share);
226
-				$shareId = $this->createFederatedShare($share);
227
-			}
228
-			if ($send) {
229
-				$this->updateSuccessfulReshare($shareId, $token);
230
-				$this->storeRemoteId($shareId, $remoteId);
231
-			} else {
232
-				$this->removeShareFromTable($share);
233
-				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
234
-				throw new \Exception($message_t);
235
-			}
236
-
237
-		} else {
238
-			$shareId = $this->createFederatedShare($share);
239
-		}
240
-
241
-		$data = $this->getRawShare($shareId);
242
-		return $this->createShareObject($data);
243
-	}
244
-
245
-	/**
246
-	 * create federated share and inform the recipient
247
-	 *
248
-	 * @param IShare $share
249
-	 * @return int
250
-	 * @throws ShareNotFound
251
-	 * @throws \Exception
252
-	 */
253
-	protected function createFederatedShare(IShare $share) {
254
-		$token = $this->tokenHandler->generateToken();
255
-		$shareId = $this->addShareToDB(
256
-			$share->getNodeId(),
257
-			$share->getNodeType(),
258
-			$share->getSharedWith(),
259
-			$share->getSharedBy(),
260
-			$share->getShareOwner(),
261
-			$share->getPermissions(),
262
-			$token,
263
-			$share->getShareType()
264
-		);
265
-
266
-		$failure = false;
267
-
268
-		try {
269
-			$sharedByFederatedId = $share->getSharedBy();
270
-			if ($this->userManager->userExists($sharedByFederatedId)) {
271
-				$cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
272
-				$sharedByFederatedId = $cloudId->getId();
273
-			}
274
-			$ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
275
-			$send = $this->notifications->sendRemoteShare(
276
-				$token,
277
-				$share->getSharedWith(),
278
-				$share->getNode()->getName(),
279
-				$shareId,
280
-				$share->getShareOwner(),
281
-				$ownerCloudId->getId(),
282
-				$share->getSharedBy(),
283
-				$sharedByFederatedId,
284
-				$share->getShareType()
285
-			);
286
-
287
-			if ($send === false) {
288
-				$failure = true;
289
-			}
290
-		} catch (\Exception $e) {
291
-			$this->logger->logException($e, [
292
-				'message' => 'Failed to notify remote server of federated share, removing share.',
293
-				'level' => ILogger::ERROR,
294
-				'app' => 'federatedfilesharing',
295
-			]);
296
-			$failure = true;
297
-		}
298
-
299
-		if($failure) {
300
-			$this->removeShareFromTableById($shareId);
301
-			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
302
-				[$share->getNode()->getName(), $share->getSharedWith()]);
303
-			throw new \Exception($message_t);
304
-		}
305
-
306
-		return $shareId;
307
-
308
-	}
309
-
310
-	/**
311
-	 * @param string $shareWith
312
-	 * @param IShare $share
313
-	 * @param string $shareId internal share Id
314
-	 * @return array
315
-	 * @throws \Exception
316
-	 */
317
-	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
318
-
319
-		$remoteShare = $this->getShareFromExternalShareTable($share);
320
-		$token = $remoteShare['share_token'];
321
-		$remoteId = $remoteShare['remote_id'];
322
-		$remote = $remoteShare['remote'];
323
-
324
-		list($token, $remoteId) = $this->notifications->requestReShare(
325
-			$token,
326
-			$remoteId,
327
-			$shareId,
328
-			$remote,
329
-			$shareWith,
330
-			$share->getPermissions(),
331
-			$share->getNode()->getName()
332
-		);
333
-
334
-		return [$token, $remoteId];
335
-	}
336
-
337
-	/**
338
-	 * get federated share from the share_external table but exclude mounted link shares
339
-	 *
340
-	 * @param IShare $share
341
-	 * @return array
342
-	 * @throws ShareNotFound
343
-	 */
344
-	protected function getShareFromExternalShareTable(IShare $share) {
345
-		$query = $this->dbConnection->getQueryBuilder();
346
-		$query->select('*')->from($this->externalShareTable)
347
-			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
348
-			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
349
-		$result = $query->execute()->fetchAll();
350
-
351
-		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
352
-			return $result[0];
353
-		}
354
-
355
-		throw new ShareNotFound('share not found in share_external table');
356
-	}
357
-
358
-	/**
359
-	 * add share to the database and return the ID
360
-	 *
361
-	 * @param int $itemSource
362
-	 * @param string $itemType
363
-	 * @param string $shareWith
364
-	 * @param string $sharedBy
365
-	 * @param string $uidOwner
366
-	 * @param int $permissions
367
-	 * @param string $token
368
-	 * @param int $shareType
369
-	 * @return int
370
-	 */
371
-	private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType) {
372
-		$qb = $this->dbConnection->getQueryBuilder();
373
-		$qb->insert('share')
374
-			->setValue('share_type', $qb->createNamedParameter($shareType))
375
-			->setValue('item_type', $qb->createNamedParameter($itemType))
376
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
377
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
378
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
379
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
380
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
381
-			->setValue('permissions', $qb->createNamedParameter($permissions))
382
-			->setValue('token', $qb->createNamedParameter($token))
383
-			->setValue('stime', $qb->createNamedParameter(time()));
384
-
385
-		/*
184
+        $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
185
+        $alreadySharedGroup = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_REMOTE_GROUP, $share->getNode(), 1, 0);
186
+        if (!empty($alreadyShared) || !empty($alreadySharedGroup)) {
187
+            $message = 'Sharing %s failed, because this item is already shared with %s';
188
+            $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
189
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
190
+            throw new \Exception($message_t);
191
+        }
192
+
193
+
194
+        // don't allow federated shares if source and target server are the same
195
+        $cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
196
+        $currentServer = $this->addressHandler->generateRemoteURL();
197
+        $currentUser = $sharedBy;
198
+        if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
199
+            $message = 'Not allowed to create a federated share with the same user.';
200
+            $message_t = $this->l->t('Not allowed to create a federated share with the same user');
201
+            $this->logger->debug($message, ['app' => 'Federated File Sharing']);
202
+            throw new \Exception($message_t);
203
+        }
204
+
205
+
206
+        $share->setSharedWith($cloudId->getId());
207
+
208
+        try {
209
+            $remoteShare = $this->getShareFromExternalShareTable($share);
210
+        } catch (ShareNotFound $e) {
211
+            $remoteShare = null;
212
+        }
213
+
214
+        if ($remoteShare) {
215
+            try {
216
+                $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
217
+                $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType);
218
+                $share->setId($shareId);
219
+                list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
220
+                // remote share was create successfully if we get a valid token as return
221
+                $send = is_string($token) && $token !== '';
222
+            } catch (\Exception $e) {
223
+                // fall back to old re-share behavior if the remote server
224
+                // doesn't support flat re-shares (was introduced with Nextcloud 9.1)
225
+                $this->removeShareFromTable($share);
226
+                $shareId = $this->createFederatedShare($share);
227
+            }
228
+            if ($send) {
229
+                $this->updateSuccessfulReshare($shareId, $token);
230
+                $this->storeRemoteId($shareId, $remoteId);
231
+            } else {
232
+                $this->removeShareFromTable($share);
233
+                $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
234
+                throw new \Exception($message_t);
235
+            }
236
+
237
+        } else {
238
+            $shareId = $this->createFederatedShare($share);
239
+        }
240
+
241
+        $data = $this->getRawShare($shareId);
242
+        return $this->createShareObject($data);
243
+    }
244
+
245
+    /**
246
+     * create federated share and inform the recipient
247
+     *
248
+     * @param IShare $share
249
+     * @return int
250
+     * @throws ShareNotFound
251
+     * @throws \Exception
252
+     */
253
+    protected function createFederatedShare(IShare $share) {
254
+        $token = $this->tokenHandler->generateToken();
255
+        $shareId = $this->addShareToDB(
256
+            $share->getNodeId(),
257
+            $share->getNodeType(),
258
+            $share->getSharedWith(),
259
+            $share->getSharedBy(),
260
+            $share->getShareOwner(),
261
+            $share->getPermissions(),
262
+            $token,
263
+            $share->getShareType()
264
+        );
265
+
266
+        $failure = false;
267
+
268
+        try {
269
+            $sharedByFederatedId = $share->getSharedBy();
270
+            if ($this->userManager->userExists($sharedByFederatedId)) {
271
+                $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
272
+                $sharedByFederatedId = $cloudId->getId();
273
+            }
274
+            $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
275
+            $send = $this->notifications->sendRemoteShare(
276
+                $token,
277
+                $share->getSharedWith(),
278
+                $share->getNode()->getName(),
279
+                $shareId,
280
+                $share->getShareOwner(),
281
+                $ownerCloudId->getId(),
282
+                $share->getSharedBy(),
283
+                $sharedByFederatedId,
284
+                $share->getShareType()
285
+            );
286
+
287
+            if ($send === false) {
288
+                $failure = true;
289
+            }
290
+        } catch (\Exception $e) {
291
+            $this->logger->logException($e, [
292
+                'message' => 'Failed to notify remote server of federated share, removing share.',
293
+                'level' => ILogger::ERROR,
294
+                'app' => 'federatedfilesharing',
295
+            ]);
296
+            $failure = true;
297
+        }
298
+
299
+        if($failure) {
300
+            $this->removeShareFromTableById($shareId);
301
+            $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
302
+                [$share->getNode()->getName(), $share->getSharedWith()]);
303
+            throw new \Exception($message_t);
304
+        }
305
+
306
+        return $shareId;
307
+
308
+    }
309
+
310
+    /**
311
+     * @param string $shareWith
312
+     * @param IShare $share
313
+     * @param string $shareId internal share Id
314
+     * @return array
315
+     * @throws \Exception
316
+     */
317
+    protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
318
+
319
+        $remoteShare = $this->getShareFromExternalShareTable($share);
320
+        $token = $remoteShare['share_token'];
321
+        $remoteId = $remoteShare['remote_id'];
322
+        $remote = $remoteShare['remote'];
323
+
324
+        list($token, $remoteId) = $this->notifications->requestReShare(
325
+            $token,
326
+            $remoteId,
327
+            $shareId,
328
+            $remote,
329
+            $shareWith,
330
+            $share->getPermissions(),
331
+            $share->getNode()->getName()
332
+        );
333
+
334
+        return [$token, $remoteId];
335
+    }
336
+
337
+    /**
338
+     * get federated share from the share_external table but exclude mounted link shares
339
+     *
340
+     * @param IShare $share
341
+     * @return array
342
+     * @throws ShareNotFound
343
+     */
344
+    protected function getShareFromExternalShareTable(IShare $share) {
345
+        $query = $this->dbConnection->getQueryBuilder();
346
+        $query->select('*')->from($this->externalShareTable)
347
+            ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
348
+            ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
349
+        $result = $query->execute()->fetchAll();
350
+
351
+        if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
352
+            return $result[0];
353
+        }
354
+
355
+        throw new ShareNotFound('share not found in share_external table');
356
+    }
357
+
358
+    /**
359
+     * add share to the database and return the ID
360
+     *
361
+     * @param int $itemSource
362
+     * @param string $itemType
363
+     * @param string $shareWith
364
+     * @param string $sharedBy
365
+     * @param string $uidOwner
366
+     * @param int $permissions
367
+     * @param string $token
368
+     * @param int $shareType
369
+     * @return int
370
+     */
371
+    private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType) {
372
+        $qb = $this->dbConnection->getQueryBuilder();
373
+        $qb->insert('share')
374
+            ->setValue('share_type', $qb->createNamedParameter($shareType))
375
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
376
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
377
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
378
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
379
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
380
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
381
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
382
+            ->setValue('token', $qb->createNamedParameter($token))
383
+            ->setValue('stime', $qb->createNamedParameter(time()));
384
+
385
+        /*
386 386
 		 * Added to fix https://github.com/owncloud/core/issues/22215
387 387
 		 * Can be removed once we get rid of ajax/share.php
388 388
 		 */
389
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
390
-
391
-		$qb->execute();
392
-		$id = $qb->getLastInsertId();
393
-
394
-		return (int)$id;
395
-	}
396
-
397
-	/**
398
-	 * Update a share
399
-	 *
400
-	 * @param IShare $share
401
-	 * @return IShare The share object
402
-	 */
403
-	public function update(IShare $share) {
404
-		/*
389
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
390
+
391
+        $qb->execute();
392
+        $id = $qb->getLastInsertId();
393
+
394
+        return (int)$id;
395
+    }
396
+
397
+    /**
398
+     * Update a share
399
+     *
400
+     * @param IShare $share
401
+     * @return IShare The share object
402
+     */
403
+    public function update(IShare $share) {
404
+        /*
405 405
 		 * We allow updating the permissions of federated shares
406 406
 		 */
407
-		$qb = $this->dbConnection->getQueryBuilder();
408
-			$qb->update('share')
409
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
410
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
411
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
412
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
413
-				->execute();
414
-
415
-		// send the updated permission to the owner/initiator, if they are not the same
416
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
417
-			$this->sendPermissionUpdate($share);
418
-		}
419
-
420
-		return $share;
421
-	}
422
-
423
-	/**
424
-	 * send the updated permission to the owner/initiator, if they are not the same
425
-	 *
426
-	 * @param IShare $share
427
-	 * @throws ShareNotFound
428
-	 * @throws \OC\HintException
429
-	 */
430
-	protected function sendPermissionUpdate(IShare $share) {
431
-		$remoteId = $this->getRemoteId($share);
432
-		// if the local user is the owner we send the permission change to the initiator
433
-		if ($this->userManager->userExists($share->getShareOwner())) {
434
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
435
-		} else { // ... if not we send the permission change to the owner
436
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
437
-		}
438
-		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
439
-	}
440
-
441
-
442
-	/**
443
-	 * update successful reShare with the correct token
444
-	 *
445
-	 * @param int $shareId
446
-	 * @param string $token
447
-	 */
448
-	protected function updateSuccessfulReShare($shareId, $token) {
449
-		$query = $this->dbConnection->getQueryBuilder();
450
-		$query->update('share')
451
-			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
452
-			->set('token', $query->createNamedParameter($token))
453
-			->execute();
454
-	}
455
-
456
-	/**
457
-	 * store remote ID in federated reShare table
458
-	 *
459
-	 * @param $shareId
460
-	 * @param $remoteId
461
-	 */
462
-	public function storeRemoteId($shareId, $remoteId) {
463
-		$query = $this->dbConnection->getQueryBuilder();
464
-		$query->insert('federated_reshares')
465
-			->values(
466
-				[
467
-					'share_id' =>  $query->createNamedParameter($shareId),
468
-					'remote_id' => $query->createNamedParameter($remoteId),
469
-				]
470
-			);
471
-		$query->execute();
472
-	}
473
-
474
-	/**
475
-	 * get share ID on remote server for federated re-shares
476
-	 *
477
-	 * @param IShare $share
478
-	 * @return int
479
-	 * @throws ShareNotFound
480
-	 */
481
-	public function getRemoteId(IShare $share) {
482
-		$query = $this->dbConnection->getQueryBuilder();
483
-		$query->select('remote_id')->from('federated_reshares')
484
-			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
485
-		$data = $query->execute()->fetch();
486
-
487
-		if (!is_array($data) || !isset($data['remote_id'])) {
488
-			throw new ShareNotFound();
489
-		}
490
-
491
-		return (int)$data['remote_id'];
492
-	}
493
-
494
-	/**
495
-	 * @inheritdoc
496
-	 */
497
-	public function move(IShare $share, $recipient) {
498
-		/*
407
+        $qb = $this->dbConnection->getQueryBuilder();
408
+            $qb->update('share')
409
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
410
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
411
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
412
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
413
+                ->execute();
414
+
415
+        // send the updated permission to the owner/initiator, if they are not the same
416
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
417
+            $this->sendPermissionUpdate($share);
418
+        }
419
+
420
+        return $share;
421
+    }
422
+
423
+    /**
424
+     * send the updated permission to the owner/initiator, if they are not the same
425
+     *
426
+     * @param IShare $share
427
+     * @throws ShareNotFound
428
+     * @throws \OC\HintException
429
+     */
430
+    protected function sendPermissionUpdate(IShare $share) {
431
+        $remoteId = $this->getRemoteId($share);
432
+        // if the local user is the owner we send the permission change to the initiator
433
+        if ($this->userManager->userExists($share->getShareOwner())) {
434
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
435
+        } else { // ... if not we send the permission change to the owner
436
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
437
+        }
438
+        $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
439
+    }
440
+
441
+
442
+    /**
443
+     * update successful reShare with the correct token
444
+     *
445
+     * @param int $shareId
446
+     * @param string $token
447
+     */
448
+    protected function updateSuccessfulReShare($shareId, $token) {
449
+        $query = $this->dbConnection->getQueryBuilder();
450
+        $query->update('share')
451
+            ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
452
+            ->set('token', $query->createNamedParameter($token))
453
+            ->execute();
454
+    }
455
+
456
+    /**
457
+     * store remote ID in federated reShare table
458
+     *
459
+     * @param $shareId
460
+     * @param $remoteId
461
+     */
462
+    public function storeRemoteId($shareId, $remoteId) {
463
+        $query = $this->dbConnection->getQueryBuilder();
464
+        $query->insert('federated_reshares')
465
+            ->values(
466
+                [
467
+                    'share_id' =>  $query->createNamedParameter($shareId),
468
+                    'remote_id' => $query->createNamedParameter($remoteId),
469
+                ]
470
+            );
471
+        $query->execute();
472
+    }
473
+
474
+    /**
475
+     * get share ID on remote server for federated re-shares
476
+     *
477
+     * @param IShare $share
478
+     * @return int
479
+     * @throws ShareNotFound
480
+     */
481
+    public function getRemoteId(IShare $share) {
482
+        $query = $this->dbConnection->getQueryBuilder();
483
+        $query->select('remote_id')->from('federated_reshares')
484
+            ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
485
+        $data = $query->execute()->fetch();
486
+
487
+        if (!is_array($data) || !isset($data['remote_id'])) {
488
+            throw new ShareNotFound();
489
+        }
490
+
491
+        return (int)$data['remote_id'];
492
+    }
493
+
494
+    /**
495
+     * @inheritdoc
496
+     */
497
+    public function move(IShare $share, $recipient) {
498
+        /*
499 499
 		 * This function does nothing yet as it is just for outgoing
500 500
 		 * federated shares.
501 501
 		 */
502
-		return $share;
503
-	}
504
-
505
-	/**
506
-	 * Get all children of this share
507
-	 *
508
-	 * @param IShare $parent
509
-	 * @return IShare[]
510
-	 */
511
-	public function getChildren(IShare $parent) {
512
-		$children = [];
513
-
514
-		$qb = $this->dbConnection->getQueryBuilder();
515
-		$qb->select('*')
516
-			->from('share')
517
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
518
-			->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
519
-			->orderBy('id');
520
-
521
-		$cursor = $qb->execute();
522
-		while($data = $cursor->fetch()) {
523
-			$children[] = $this->createShareObject($data);
524
-		}
525
-		$cursor->closeCursor();
526
-
527
-		return $children;
528
-	}
529
-
530
-	/**
531
-	 * Delete a share (owner unShares the file)
532
-	 *
533
-	 * @param IShare $share
534
-	 * @throws ShareNotFound
535
-	 * @throws \OC\HintException
536
-	 */
537
-	public function delete(IShare $share) {
538
-
539
-		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
540
-
541
-		// if the local user is the owner we can send the unShare request directly...
542
-		if ($this->userManager->userExists($share->getShareOwner())) {
543
-			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
544
-			$this->revokeShare($share, true);
545
-		} else { // ... if not we need to correct ID for the unShare request
546
-			$remoteId = $this->getRemoteId($share);
547
-			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
548
-			$this->revokeShare($share, false);
549
-		}
550
-
551
-		// only remove the share when all messages are send to not lose information
552
-		// about the share to early
553
-		$this->removeShareFromTable($share);
554
-	}
555
-
556
-	/**
557
-	 * in case of a re-share we need to send the other use (initiator or owner)
558
-	 * a message that the file was unshared
559
-	 *
560
-	 * @param IShare $share
561
-	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
562
-	 * @throws ShareNotFound
563
-	 * @throws \OC\HintException
564
-	 */
565
-	protected function revokeShare($share, $isOwner) {
566
-		// also send a unShare request to the initiator, if this is a different user than the owner
567
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
568
-			if ($isOwner) {
569
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
570
-			} else {
571
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
572
-			}
573
-			$remoteId = $this->getRemoteId($share);
574
-			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
575
-		}
576
-	}
577
-
578
-	/**
579
-	 * remove share from table
580
-	 *
581
-	 * @param IShare $share
582
-	 */
583
-	public function removeShareFromTable(IShare $share) {
584
-		$this->removeShareFromTableById($share->getId());
585
-	}
586
-
587
-	/**
588
-	 * remove share from table
589
-	 *
590
-	 * @param string $shareId
591
-	 */
592
-	private function removeShareFromTableById($shareId) {
593
-		$qb = $this->dbConnection->getQueryBuilder();
594
-		$qb->delete('share')
595
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
596
-		$qb->execute();
597
-
598
-		$qb->delete('federated_reshares')
599
-			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
600
-		$qb->execute();
601
-	}
602
-
603
-	/**
604
-	 * @inheritdoc
605
-	 */
606
-	public function deleteFromSelf(IShare $share, $recipient) {
607
-		// nothing to do here. Technically deleteFromSelf in the context of federated
608
-		// shares is a umount of a external storage. This is handled here
609
-		// apps/files_sharing/lib/external/manager.php
610
-		// TODO move this code over to this app
611
-	}
612
-
613
-	public function restore(IShare $share, string $recipient): IShare {
614
-		throw new GenericShareException('not implemented');
615
-	}
616
-
617
-
618
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
619
-		$qb = $this->dbConnection->getQueryBuilder();
620
-		$qb->select('*')
621
-			->from('share', 's')
622
-			->andWhere($qb->expr()->orX(
623
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
624
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
625
-			))
626
-			->andWhere(
627
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
628
-			);
629
-
630
-		/**
631
-		 * Reshares for this user are shares where they are the owner.
632
-		 */
633
-		if ($reshares === false) {
634
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
635
-		} else {
636
-			$qb->andWhere(
637
-				$qb->expr()->orX(
638
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
639
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
640
-				)
641
-			);
642
-		}
643
-
644
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
645
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
646
-
647
-		$qb->orderBy('id');
648
-
649
-		$cursor = $qb->execute();
650
-		$shares = [];
651
-		while ($data = $cursor->fetch()) {
652
-			$shares[$data['fileid']][] = $this->createShareObject($data);
653
-		}
654
-		$cursor->closeCursor();
655
-
656
-		return $shares;
657
-	}
658
-
659
-	/**
660
-	 * @inheritdoc
661
-	 */
662
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
663
-		$qb = $this->dbConnection->getQueryBuilder();
664
-		$qb->select('*')
665
-			->from('share');
666
-
667
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
668
-
669
-		/**
670
-		 * Reshares for this user are shares where they are the owner.
671
-		 */
672
-		if ($reshares === false) {
673
-			//Special case for old shares created via the web UI
674
-			$or1 = $qb->expr()->andX(
675
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
676
-				$qb->expr()->isNull('uid_initiator')
677
-			);
678
-
679
-			$qb->andWhere(
680
-				$qb->expr()->orX(
681
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
682
-					$or1
683
-				)
684
-			);
685
-		} else {
686
-			$qb->andWhere(
687
-				$qb->expr()->orX(
688
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
689
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
690
-				)
691
-			);
692
-		}
693
-
694
-		if ($node !== null) {
695
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
696
-		}
697
-
698
-		if ($limit !== -1) {
699
-			$qb->setMaxResults($limit);
700
-		}
701
-
702
-		$qb->setFirstResult($offset);
703
-		$qb->orderBy('id');
704
-
705
-		$cursor = $qb->execute();
706
-		$shares = [];
707
-		while($data = $cursor->fetch()) {
708
-			$shares[] = $this->createShareObject($data);
709
-		}
710
-		$cursor->closeCursor();
711
-
712
-		return $shares;
713
-	}
714
-
715
-	/**
716
-	 * @inheritdoc
717
-	 */
718
-	public function getShareById($id, $recipientId = null) {
719
-		$qb = $this->dbConnection->getQueryBuilder();
720
-
721
-		$qb->select('*')
722
-			->from('share')
723
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
724
-			->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
725
-
726
-		$cursor = $qb->execute();
727
-		$data = $cursor->fetch();
728
-		$cursor->closeCursor();
729
-
730
-		if ($data === false) {
731
-			throw new ShareNotFound('Can not find share with ID: ' . $id);
732
-		}
733
-
734
-		try {
735
-			$share = $this->createShareObject($data);
736
-		} catch (InvalidShare $e) {
737
-			throw new ShareNotFound();
738
-		}
739
-
740
-		return $share;
741
-	}
742
-
743
-	/**
744
-	 * Get shares for a given path
745
-	 *
746
-	 * @param \OCP\Files\Node $path
747
-	 * @return IShare[]
748
-	 */
749
-	public function getSharesByPath(Node $path) {
750
-		$qb = $this->dbConnection->getQueryBuilder();
751
-
752
-		// get federated user shares
753
-		$cursor = $qb->select('*')
754
-			->from('share')
755
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
756
-			->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
757
-			->execute();
758
-
759
-		$shares = [];
760
-		while($data = $cursor->fetch()) {
761
-			$shares[] = $this->createShareObject($data);
762
-		}
763
-		$cursor->closeCursor();
764
-
765
-		return $shares;
766
-	}
767
-
768
-	/**
769
-	 * @inheritdoc
770
-	 */
771
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
772
-		/** @var IShare[] $shares */
773
-		$shares = [];
774
-
775
-		//Get shares directly with this user
776
-		$qb = $this->dbConnection->getQueryBuilder();
777
-		$qb->select('*')
778
-			->from('share');
779
-
780
-		// Order by id
781
-		$qb->orderBy('id');
782
-
783
-		// Set limit and offset
784
-		if ($limit !== -1) {
785
-			$qb->setMaxResults($limit);
786
-		}
787
-		$qb->setFirstResult($offset);
788
-
789
-		$qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
790
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
791
-
792
-		// Filter by node if provided
793
-		if ($node !== null) {
794
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
795
-		}
796
-
797
-		$cursor = $qb->execute();
798
-
799
-		while($data = $cursor->fetch()) {
800
-			$shares[] = $this->createShareObject($data);
801
-		}
802
-		$cursor->closeCursor();
803
-
804
-
805
-		return $shares;
806
-	}
807
-
808
-	/**
809
-	 * Get a share by token
810
-	 *
811
-	 * @param string $token
812
-	 * @return IShare
813
-	 * @throws ShareNotFound
814
-	 */
815
-	public function getShareByToken($token) {
816
-		$qb = $this->dbConnection->getQueryBuilder();
817
-
818
-		$cursor = $qb->select('*')
819
-			->from('share')
820
-			->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
821
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
822
-			->execute();
823
-
824
-		$data = $cursor->fetch();
825
-
826
-		if ($data === false) {
827
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
828
-		}
829
-
830
-		try {
831
-			$share = $this->createShareObject($data);
832
-		} catch (InvalidShare $e) {
833
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
834
-		}
835
-
836
-		return $share;
837
-	}
838
-
839
-	/**
840
-	 * get database row of a give share
841
-	 *
842
-	 * @param $id
843
-	 * @return array
844
-	 * @throws ShareNotFound
845
-	 */
846
-	private function getRawShare($id) {
847
-
848
-		// Now fetch the inserted share and create a complete share object
849
-		$qb = $this->dbConnection->getQueryBuilder();
850
-		$qb->select('*')
851
-			->from('share')
852
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
853
-
854
-		$cursor = $qb->execute();
855
-		$data = $cursor->fetch();
856
-		$cursor->closeCursor();
857
-
858
-		if ($data === false) {
859
-			throw new ShareNotFound;
860
-		}
861
-
862
-		return $data;
863
-	}
864
-
865
-	/**
866
-	 * Create a share object from an database row
867
-	 *
868
-	 * @param array $data
869
-	 * @return IShare
870
-	 * @throws InvalidShare
871
-	 * @throws ShareNotFound
872
-	 */
873
-	private function createShareObject($data) {
874
-
875
-		$share = new Share($this->rootFolder, $this->userManager);
876
-		$share->setId((int)$data['id'])
877
-			->setShareType((int)$data['share_type'])
878
-			->setPermissions((int)$data['permissions'])
879
-			->setTarget($data['file_target'])
880
-			->setMailSend((bool)$data['mail_send'])
881
-			->setToken($data['token']);
882
-
883
-		$shareTime = new \DateTime();
884
-		$shareTime->setTimestamp((int)$data['stime']);
885
-		$share->setShareTime($shareTime);
886
-		$share->setSharedWith($data['share_with']);
887
-
888
-		if ($data['uid_initiator'] !== null) {
889
-			$share->setShareOwner($data['uid_owner']);
890
-			$share->setSharedBy($data['uid_initiator']);
891
-		} else {
892
-			//OLD SHARE
893
-			$share->setSharedBy($data['uid_owner']);
894
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
895
-
896
-			$owner = $path->getOwner();
897
-			$share->setShareOwner($owner->getUID());
898
-		}
899
-
900
-		$share->setNodeId((int)$data['file_source']);
901
-		$share->setNodeType($data['item_type']);
902
-
903
-		$share->setProviderId($this->identifier());
904
-
905
-		return $share;
906
-	}
907
-
908
-	/**
909
-	 * Get the node with file $id for $user
910
-	 *
911
-	 * @param string $userId
912
-	 * @param int $id
913
-	 * @return \OCP\Files\File|\OCP\Files\Folder
914
-	 * @throws InvalidShare
915
-	 */
916
-	private function getNode($userId, $id) {
917
-		try {
918
-			$userFolder = $this->rootFolder->getUserFolder($userId);
919
-		} catch (NotFoundException $e) {
920
-			throw new InvalidShare();
921
-		}
922
-
923
-		$nodes = $userFolder->getById($id);
924
-
925
-		if (empty($nodes)) {
926
-			throw new InvalidShare();
927
-		}
928
-
929
-		return $nodes[0];
930
-	}
931
-
932
-	/**
933
-	 * A user is deleted from the system
934
-	 * So clean up the relevant shares.
935
-	 *
936
-	 * @param string $uid
937
-	 * @param int $shareType
938
-	 */
939
-	public function userDeleted($uid, $shareType) {
940
-		//TODO: probabaly a good idea to send unshare info to remote servers
941
-
942
-		$qb = $this->dbConnection->getQueryBuilder();
943
-
944
-		$qb->delete('share')
945
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
946
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
947
-			->execute();
948
-	}
949
-
950
-	/**
951
-	 * This provider does not handle groups
952
-	 *
953
-	 * @param string $gid
954
-	 */
955
-	public function groupDeleted($gid) {
956
-		// We don't handle groups here
957
-	}
958
-
959
-	/**
960
-	 * This provider does not handle groups
961
-	 *
962
-	 * @param string $uid
963
-	 * @param string $gid
964
-	 */
965
-	public function userDeletedFromGroup($uid, $gid) {
966
-		// We don't handle groups here
967
-	}
968
-
969
-	/**
970
-	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
971
-	 *
972
-	 * @return bool
973
-	 */
974
-	public function isOutgoingServer2serverShareEnabled() {
975
-		if ($this->gsConfig->onlyInternalFederation()) {
976
-			return false;
977
-		}
978
-		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
979
-		return ($result === 'yes');
980
-	}
981
-
982
-	/**
983
-	 * check if users are allowed to mount public links from other Nextclouds
984
-	 *
985
-	 * @return bool
986
-	 */
987
-	public function isIncomingServer2serverShareEnabled() {
988
-		if ($this->gsConfig->onlyInternalFederation()) {
989
-			return false;
990
-		}
991
-		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
992
-		return ($result === 'yes');
993
-	}
994
-
995
-
996
-	/**
997
-	 * check if users from other Nextcloud instances are allowed to send federated group shares
998
-	 *
999
-	 * @return bool
1000
-	 */
1001
-	public function isOutgoingServer2serverGroupShareEnabled() {
1002
-		if ($this->gsConfig->onlyInternalFederation()) {
1003
-			return false;
1004
-		}
1005
-		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no');
1006
-		return ($result === 'yes');
1007
-	}
1008
-
1009
-	/**
1010
-	 * check if users are allowed to receive federated group shares
1011
-	 *
1012
-	 * @return bool
1013
-	 */
1014
-	public function isIncomingServer2serverGroupShareEnabled() {
1015
-		if ($this->gsConfig->onlyInternalFederation()) {
1016
-			return false;
1017
-		}
1018
-		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no');
1019
-		return ($result === 'yes');
1020
-	}
1021
-
1022
-	/**
1023
-	 * check if federated group sharing is supported, therefore the OCM API need to be enabled
1024
-	 *
1025
-	 * @return bool
1026
-	 */
1027
-	public function isFederatedGroupSharingSupported() {
1028
-		return $this->cloudFederationProviderManager->isReady();
1029
-	}
1030
-
1031
-	/**
1032
-	 * Check if querying sharees on the lookup server is enabled
1033
-	 *
1034
-	 * @return bool
1035
-	 */
1036
-	public function isLookupServerQueriesEnabled() {
1037
-		// in a global scale setup we should always query the lookup server
1038
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
1039
-			return true;
1040
-		}
1041
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
1042
-		return ($result === 'yes');
1043
-	}
1044
-
1045
-
1046
-	/**
1047
-	 * Check if it is allowed to publish user specific data to the lookup server
1048
-	 *
1049
-	 * @return bool
1050
-	 */
1051
-	public function isLookupServerUploadEnabled() {
1052
-		// in a global scale setup the admin is responsible to keep the lookup server up-to-date
1053
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
1054
-			return false;
1055
-		}
1056
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
1057
-		return ($result === 'yes');
1058
-	}
1059
-
1060
-	/**
1061
-	 * @inheritdoc
1062
-	 */
1063
-	public function getAccessList($nodes, $currentAccess) {
1064
-		$ids = [];
1065
-		foreach ($nodes as $node) {
1066
-			$ids[] = $node->getId();
1067
-		}
1068
-
1069
-		$qb = $this->dbConnection->getQueryBuilder();
1070
-		$qb->select('share_with', 'token', 'file_source')
1071
-			->from('share')
1072
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1073
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1074
-			->andWhere($qb->expr()->orX(
1075
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1076
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1077
-			));
1078
-		$cursor = $qb->execute();
1079
-
1080
-		if ($currentAccess === false) {
1081
-			$remote = $cursor->fetch() !== false;
1082
-			$cursor->closeCursor();
1083
-
1084
-			return ['remote' => $remote];
1085
-		}
1086
-
1087
-		$remote = [];
1088
-		while ($row = $cursor->fetch()) {
1089
-			$remote[$row['share_with']] = [
1090
-				'node_id' => $row['file_source'],
1091
-				'token' => $row['token'],
1092
-			];
1093
-		}
1094
-		$cursor->closeCursor();
1095
-
1096
-		return ['remote' => $remote];
1097
-	}
502
+        return $share;
503
+    }
504
+
505
+    /**
506
+     * Get all children of this share
507
+     *
508
+     * @param IShare $parent
509
+     * @return IShare[]
510
+     */
511
+    public function getChildren(IShare $parent) {
512
+        $children = [];
513
+
514
+        $qb = $this->dbConnection->getQueryBuilder();
515
+        $qb->select('*')
516
+            ->from('share')
517
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
518
+            ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
519
+            ->orderBy('id');
520
+
521
+        $cursor = $qb->execute();
522
+        while($data = $cursor->fetch()) {
523
+            $children[] = $this->createShareObject($data);
524
+        }
525
+        $cursor->closeCursor();
526
+
527
+        return $children;
528
+    }
529
+
530
+    /**
531
+     * Delete a share (owner unShares the file)
532
+     *
533
+     * @param IShare $share
534
+     * @throws ShareNotFound
535
+     * @throws \OC\HintException
536
+     */
537
+    public function delete(IShare $share) {
538
+
539
+        list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
540
+
541
+        // if the local user is the owner we can send the unShare request directly...
542
+        if ($this->userManager->userExists($share->getShareOwner())) {
543
+            $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
544
+            $this->revokeShare($share, true);
545
+        } else { // ... if not we need to correct ID for the unShare request
546
+            $remoteId = $this->getRemoteId($share);
547
+            $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
548
+            $this->revokeShare($share, false);
549
+        }
550
+
551
+        // only remove the share when all messages are send to not lose information
552
+        // about the share to early
553
+        $this->removeShareFromTable($share);
554
+    }
555
+
556
+    /**
557
+     * in case of a re-share we need to send the other use (initiator or owner)
558
+     * a message that the file was unshared
559
+     *
560
+     * @param IShare $share
561
+     * @param bool $isOwner the user can either be the owner or the user who re-sahred it
562
+     * @throws ShareNotFound
563
+     * @throws \OC\HintException
564
+     */
565
+    protected function revokeShare($share, $isOwner) {
566
+        // also send a unShare request to the initiator, if this is a different user than the owner
567
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
568
+            if ($isOwner) {
569
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
570
+            } else {
571
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
572
+            }
573
+            $remoteId = $this->getRemoteId($share);
574
+            $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
575
+        }
576
+    }
577
+
578
+    /**
579
+     * remove share from table
580
+     *
581
+     * @param IShare $share
582
+     */
583
+    public function removeShareFromTable(IShare $share) {
584
+        $this->removeShareFromTableById($share->getId());
585
+    }
586
+
587
+    /**
588
+     * remove share from table
589
+     *
590
+     * @param string $shareId
591
+     */
592
+    private function removeShareFromTableById($shareId) {
593
+        $qb = $this->dbConnection->getQueryBuilder();
594
+        $qb->delete('share')
595
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
596
+        $qb->execute();
597
+
598
+        $qb->delete('federated_reshares')
599
+            ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
600
+        $qb->execute();
601
+    }
602
+
603
+    /**
604
+     * @inheritdoc
605
+     */
606
+    public function deleteFromSelf(IShare $share, $recipient) {
607
+        // nothing to do here. Technically deleteFromSelf in the context of federated
608
+        // shares is a umount of a external storage. This is handled here
609
+        // apps/files_sharing/lib/external/manager.php
610
+        // TODO move this code over to this app
611
+    }
612
+
613
+    public function restore(IShare $share, string $recipient): IShare {
614
+        throw new GenericShareException('not implemented');
615
+    }
616
+
617
+
618
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
619
+        $qb = $this->dbConnection->getQueryBuilder();
620
+        $qb->select('*')
621
+            ->from('share', 's')
622
+            ->andWhere($qb->expr()->orX(
623
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
624
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
625
+            ))
626
+            ->andWhere(
627
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
628
+            );
629
+
630
+        /**
631
+         * Reshares for this user are shares where they are the owner.
632
+         */
633
+        if ($reshares === false) {
634
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
635
+        } else {
636
+            $qb->andWhere(
637
+                $qb->expr()->orX(
638
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
639
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
640
+                )
641
+            );
642
+        }
643
+
644
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
645
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
646
+
647
+        $qb->orderBy('id');
648
+
649
+        $cursor = $qb->execute();
650
+        $shares = [];
651
+        while ($data = $cursor->fetch()) {
652
+            $shares[$data['fileid']][] = $this->createShareObject($data);
653
+        }
654
+        $cursor->closeCursor();
655
+
656
+        return $shares;
657
+    }
658
+
659
+    /**
660
+     * @inheritdoc
661
+     */
662
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
663
+        $qb = $this->dbConnection->getQueryBuilder();
664
+        $qb->select('*')
665
+            ->from('share');
666
+
667
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType)));
668
+
669
+        /**
670
+         * Reshares for this user are shares where they are the owner.
671
+         */
672
+        if ($reshares === false) {
673
+            //Special case for old shares created via the web UI
674
+            $or1 = $qb->expr()->andX(
675
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
676
+                $qb->expr()->isNull('uid_initiator')
677
+            );
678
+
679
+            $qb->andWhere(
680
+                $qb->expr()->orX(
681
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
682
+                    $or1
683
+                )
684
+            );
685
+        } else {
686
+            $qb->andWhere(
687
+                $qb->expr()->orX(
688
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
689
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
690
+                )
691
+            );
692
+        }
693
+
694
+        if ($node !== null) {
695
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
696
+        }
697
+
698
+        if ($limit !== -1) {
699
+            $qb->setMaxResults($limit);
700
+        }
701
+
702
+        $qb->setFirstResult($offset);
703
+        $qb->orderBy('id');
704
+
705
+        $cursor = $qb->execute();
706
+        $shares = [];
707
+        while($data = $cursor->fetch()) {
708
+            $shares[] = $this->createShareObject($data);
709
+        }
710
+        $cursor->closeCursor();
711
+
712
+        return $shares;
713
+    }
714
+
715
+    /**
716
+     * @inheritdoc
717
+     */
718
+    public function getShareById($id, $recipientId = null) {
719
+        $qb = $this->dbConnection->getQueryBuilder();
720
+
721
+        $qb->select('*')
722
+            ->from('share')
723
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
724
+            ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
725
+
726
+        $cursor = $qb->execute();
727
+        $data = $cursor->fetch();
728
+        $cursor->closeCursor();
729
+
730
+        if ($data === false) {
731
+            throw new ShareNotFound('Can not find share with ID: ' . $id);
732
+        }
733
+
734
+        try {
735
+            $share = $this->createShareObject($data);
736
+        } catch (InvalidShare $e) {
737
+            throw new ShareNotFound();
738
+        }
739
+
740
+        return $share;
741
+    }
742
+
743
+    /**
744
+     * Get shares for a given path
745
+     *
746
+     * @param \OCP\Files\Node $path
747
+     * @return IShare[]
748
+     */
749
+    public function getSharesByPath(Node $path) {
750
+        $qb = $this->dbConnection->getQueryBuilder();
751
+
752
+        // get federated user shares
753
+        $cursor = $qb->select('*')
754
+            ->from('share')
755
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
756
+            ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
757
+            ->execute();
758
+
759
+        $shares = [];
760
+        while($data = $cursor->fetch()) {
761
+            $shares[] = $this->createShareObject($data);
762
+        }
763
+        $cursor->closeCursor();
764
+
765
+        return $shares;
766
+    }
767
+
768
+    /**
769
+     * @inheritdoc
770
+     */
771
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
772
+        /** @var IShare[] $shares */
773
+        $shares = [];
774
+
775
+        //Get shares directly with this user
776
+        $qb = $this->dbConnection->getQueryBuilder();
777
+        $qb->select('*')
778
+            ->from('share');
779
+
780
+        // Order by id
781
+        $qb->orderBy('id');
782
+
783
+        // Set limit and offset
784
+        if ($limit !== -1) {
785
+            $qb->setMaxResults($limit);
786
+        }
787
+        $qb->setFirstResult($offset);
788
+
789
+        $qb->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)));
790
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
791
+
792
+        // Filter by node if provided
793
+        if ($node !== null) {
794
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
795
+        }
796
+
797
+        $cursor = $qb->execute();
798
+
799
+        while($data = $cursor->fetch()) {
800
+            $shares[] = $this->createShareObject($data);
801
+        }
802
+        $cursor->closeCursor();
803
+
804
+
805
+        return $shares;
806
+    }
807
+
808
+    /**
809
+     * Get a share by token
810
+     *
811
+     * @param string $token
812
+     * @return IShare
813
+     * @throws ShareNotFound
814
+     */
815
+    public function getShareByToken($token) {
816
+        $qb = $this->dbConnection->getQueryBuilder();
817
+
818
+        $cursor = $qb->select('*')
819
+            ->from('share')
820
+            ->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY)))
821
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
822
+            ->execute();
823
+
824
+        $data = $cursor->fetch();
825
+
826
+        if ($data === false) {
827
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
828
+        }
829
+
830
+        try {
831
+            $share = $this->createShareObject($data);
832
+        } catch (InvalidShare $e) {
833
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
834
+        }
835
+
836
+        return $share;
837
+    }
838
+
839
+    /**
840
+     * get database row of a give share
841
+     *
842
+     * @param $id
843
+     * @return array
844
+     * @throws ShareNotFound
845
+     */
846
+    private function getRawShare($id) {
847
+
848
+        // Now fetch the inserted share and create a complete share object
849
+        $qb = $this->dbConnection->getQueryBuilder();
850
+        $qb->select('*')
851
+            ->from('share')
852
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
853
+
854
+        $cursor = $qb->execute();
855
+        $data = $cursor->fetch();
856
+        $cursor->closeCursor();
857
+
858
+        if ($data === false) {
859
+            throw new ShareNotFound;
860
+        }
861
+
862
+        return $data;
863
+    }
864
+
865
+    /**
866
+     * Create a share object from an database row
867
+     *
868
+     * @param array $data
869
+     * @return IShare
870
+     * @throws InvalidShare
871
+     * @throws ShareNotFound
872
+     */
873
+    private function createShareObject($data) {
874
+
875
+        $share = new Share($this->rootFolder, $this->userManager);
876
+        $share->setId((int)$data['id'])
877
+            ->setShareType((int)$data['share_type'])
878
+            ->setPermissions((int)$data['permissions'])
879
+            ->setTarget($data['file_target'])
880
+            ->setMailSend((bool)$data['mail_send'])
881
+            ->setToken($data['token']);
882
+
883
+        $shareTime = new \DateTime();
884
+        $shareTime->setTimestamp((int)$data['stime']);
885
+        $share->setShareTime($shareTime);
886
+        $share->setSharedWith($data['share_with']);
887
+
888
+        if ($data['uid_initiator'] !== null) {
889
+            $share->setShareOwner($data['uid_owner']);
890
+            $share->setSharedBy($data['uid_initiator']);
891
+        } else {
892
+            //OLD SHARE
893
+            $share->setSharedBy($data['uid_owner']);
894
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
895
+
896
+            $owner = $path->getOwner();
897
+            $share->setShareOwner($owner->getUID());
898
+        }
899
+
900
+        $share->setNodeId((int)$data['file_source']);
901
+        $share->setNodeType($data['item_type']);
902
+
903
+        $share->setProviderId($this->identifier());
904
+
905
+        return $share;
906
+    }
907
+
908
+    /**
909
+     * Get the node with file $id for $user
910
+     *
911
+     * @param string $userId
912
+     * @param int $id
913
+     * @return \OCP\Files\File|\OCP\Files\Folder
914
+     * @throws InvalidShare
915
+     */
916
+    private function getNode($userId, $id) {
917
+        try {
918
+            $userFolder = $this->rootFolder->getUserFolder($userId);
919
+        } catch (NotFoundException $e) {
920
+            throw new InvalidShare();
921
+        }
922
+
923
+        $nodes = $userFolder->getById($id);
924
+
925
+        if (empty($nodes)) {
926
+            throw new InvalidShare();
927
+        }
928
+
929
+        return $nodes[0];
930
+    }
931
+
932
+    /**
933
+     * A user is deleted from the system
934
+     * So clean up the relevant shares.
935
+     *
936
+     * @param string $uid
937
+     * @param int $shareType
938
+     */
939
+    public function userDeleted($uid, $shareType) {
940
+        //TODO: probabaly a good idea to send unshare info to remote servers
941
+
942
+        $qb = $this->dbConnection->getQueryBuilder();
943
+
944
+        $qb->delete('share')
945
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
946
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
947
+            ->execute();
948
+    }
949
+
950
+    /**
951
+     * This provider does not handle groups
952
+     *
953
+     * @param string $gid
954
+     */
955
+    public function groupDeleted($gid) {
956
+        // We don't handle groups here
957
+    }
958
+
959
+    /**
960
+     * This provider does not handle groups
961
+     *
962
+     * @param string $uid
963
+     * @param string $gid
964
+     */
965
+    public function userDeletedFromGroup($uid, $gid) {
966
+        // We don't handle groups here
967
+    }
968
+
969
+    /**
970
+     * check if users from other Nextcloud instances are allowed to mount public links share by this instance
971
+     *
972
+     * @return bool
973
+     */
974
+    public function isOutgoingServer2serverShareEnabled() {
975
+        if ($this->gsConfig->onlyInternalFederation()) {
976
+            return false;
977
+        }
978
+        $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
979
+        return ($result === 'yes');
980
+    }
981
+
982
+    /**
983
+     * check if users are allowed to mount public links from other Nextclouds
984
+     *
985
+     * @return bool
986
+     */
987
+    public function isIncomingServer2serverShareEnabled() {
988
+        if ($this->gsConfig->onlyInternalFederation()) {
989
+            return false;
990
+        }
991
+        $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
992
+        return ($result === 'yes');
993
+    }
994
+
995
+
996
+    /**
997
+     * check if users from other Nextcloud instances are allowed to send federated group shares
998
+     *
999
+     * @return bool
1000
+     */
1001
+    public function isOutgoingServer2serverGroupShareEnabled() {
1002
+        if ($this->gsConfig->onlyInternalFederation()) {
1003
+            return false;
1004
+        }
1005
+        $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no');
1006
+        return ($result === 'yes');
1007
+    }
1008
+
1009
+    /**
1010
+     * check if users are allowed to receive federated group shares
1011
+     *
1012
+     * @return bool
1013
+     */
1014
+    public function isIncomingServer2serverGroupShareEnabled() {
1015
+        if ($this->gsConfig->onlyInternalFederation()) {
1016
+            return false;
1017
+        }
1018
+        $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no');
1019
+        return ($result === 'yes');
1020
+    }
1021
+
1022
+    /**
1023
+     * check if federated group sharing is supported, therefore the OCM API need to be enabled
1024
+     *
1025
+     * @return bool
1026
+     */
1027
+    public function isFederatedGroupSharingSupported() {
1028
+        return $this->cloudFederationProviderManager->isReady();
1029
+    }
1030
+
1031
+    /**
1032
+     * Check if querying sharees on the lookup server is enabled
1033
+     *
1034
+     * @return bool
1035
+     */
1036
+    public function isLookupServerQueriesEnabled() {
1037
+        // in a global scale setup we should always query the lookup server
1038
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
1039
+            return true;
1040
+        }
1041
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
1042
+        return ($result === 'yes');
1043
+    }
1044
+
1045
+
1046
+    /**
1047
+     * Check if it is allowed to publish user specific data to the lookup server
1048
+     *
1049
+     * @return bool
1050
+     */
1051
+    public function isLookupServerUploadEnabled() {
1052
+        // in a global scale setup the admin is responsible to keep the lookup server up-to-date
1053
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
1054
+            return false;
1055
+        }
1056
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
1057
+        return ($result === 'yes');
1058
+    }
1059
+
1060
+    /**
1061
+     * @inheritdoc
1062
+     */
1063
+    public function getAccessList($nodes, $currentAccess) {
1064
+        $ids = [];
1065
+        foreach ($nodes as $node) {
1066
+            $ids[] = $node->getId();
1067
+        }
1068
+
1069
+        $qb = $this->dbConnection->getQueryBuilder();
1070
+        $qb->select('share_with', 'token', 'file_source')
1071
+            ->from('share')
1072
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1073
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1074
+            ->andWhere($qb->expr()->orX(
1075
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1076
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1077
+            ));
1078
+        $cursor = $qb->execute();
1079
+
1080
+        if ($currentAccess === false) {
1081
+            $remote = $cursor->fetch() !== false;
1082
+            $cursor->closeCursor();
1083
+
1084
+            return ['remote' => $remote];
1085
+        }
1086
+
1087
+        $remote = [];
1088
+        while ($row = $cursor->fetch()) {
1089
+            $remote[$row['share_with']] = [
1090
+                'node_id' => $row['file_source'],
1091
+                'token' => $row['token'],
1092
+            ];
1093
+        }
1094
+        $cursor->closeCursor();
1095
+
1096
+        return ['remote' => $remote];
1097
+    }
1098 1098
 }
Please login to merge, or discard this patch.
Spacing   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 		if ($remoteShare) {
215 215
 			try {
216 216
 				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
217
-				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType);
217
+				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_'.time(), $shareType);
218 218
 				$share->setId($shareId);
219 219
 				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
220 220
 				// remote share was create successfully if we get a valid token as return
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 			$failure = true;
297 297
 		}
298 298
 
299
-		if($failure) {
299
+		if ($failure) {
300 300
 			$this->removeShareFromTableById($shareId);
301 301
 			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
302 302
 				[$share->getNode()->getName(), $share->getSharedWith()]);
@@ -348,7 +348,7 @@  discard block
 block discarded – undo
348 348
 			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
349 349
 		$result = $query->execute()->fetchAll();
350 350
 
351
-		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
351
+		if (isset($result[0]) && (int) $result[0]['remote_id'] > 0) {
352 352
 			return $result[0];
353 353
 		}
354 354
 
@@ -391,7 +391,7 @@  discard block
 block discarded – undo
391 391
 		$qb->execute();
392 392
 		$id = $qb->getLastInsertId();
393 393
 
394
-		return (int)$id;
394
+		return (int) $id;
395 395
 	}
396 396
 
397 397
 	/**
@@ -481,14 +481,14 @@  discard block
 block discarded – undo
481 481
 	public function getRemoteId(IShare $share) {
482 482
 		$query = $this->dbConnection->getQueryBuilder();
483 483
 		$query->select('remote_id')->from('federated_reshares')
484
-			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
484
+			->where($query->expr()->eq('share_id', $query->createNamedParameter((int) $share->getId())));
485 485
 		$data = $query->execute()->fetch();
486 486
 
487 487
 		if (!is_array($data) || !isset($data['remote_id'])) {
488 488
 			throw new ShareNotFound();
489 489
 		}
490 490
 
491
-		return (int)$data['remote_id'];
491
+		return (int) $data['remote_id'];
492 492
 	}
493 493
 
494 494
 	/**
@@ -519,7 +519,7 @@  discard block
 block discarded – undo
519 519
 			->orderBy('id');
520 520
 
521 521
 		$cursor = $qb->execute();
522
-		while($data = $cursor->fetch()) {
522
+		while ($data = $cursor->fetch()) {
523 523
 			$children[] = $this->createShareObject($data);
524 524
 		}
525 525
 		$cursor->closeCursor();
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
 			);
642 642
 		}
643 643
 
644
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
644
+		$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
645 645
 		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
646 646
 
647 647
 		$qb->orderBy('id');
@@ -704,7 +704,7 @@  discard block
 block discarded – undo
704 704
 
705 705
 		$cursor = $qb->execute();
706 706
 		$shares = [];
707
-		while($data = $cursor->fetch()) {
707
+		while ($data = $cursor->fetch()) {
708 708
 			$shares[] = $this->createShareObject($data);
709 709
 		}
710 710
 		$cursor->closeCursor();
@@ -728,7 +728,7 @@  discard block
 block discarded – undo
728 728
 		$cursor->closeCursor();
729 729
 
730 730
 		if ($data === false) {
731
-			throw new ShareNotFound('Can not find share with ID: ' . $id);
731
+			throw new ShareNotFound('Can not find share with ID: '.$id);
732 732
 		}
733 733
 
734 734
 		try {
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
 			->execute();
758 758
 
759 759
 		$shares = [];
760
-		while($data = $cursor->fetch()) {
760
+		while ($data = $cursor->fetch()) {
761 761
 			$shares[] = $this->createShareObject($data);
762 762
 		}
763 763
 		$cursor->closeCursor();
@@ -796,7 +796,7 @@  discard block
 block discarded – undo
796 796
 
797 797
 		$cursor = $qb->execute();
798 798
 
799
-		while($data = $cursor->fetch()) {
799
+		while ($data = $cursor->fetch()) {
800 800
 			$shares[] = $this->createShareObject($data);
801 801
 		}
802 802
 		$cursor->closeCursor();
@@ -873,15 +873,15 @@  discard block
 block discarded – undo
873 873
 	private function createShareObject($data) {
874 874
 
875 875
 		$share = new Share($this->rootFolder, $this->userManager);
876
-		$share->setId((int)$data['id'])
877
-			->setShareType((int)$data['share_type'])
878
-			->setPermissions((int)$data['permissions'])
876
+		$share->setId((int) $data['id'])
877
+			->setShareType((int) $data['share_type'])
878
+			->setPermissions((int) $data['permissions'])
879 879
 			->setTarget($data['file_target'])
880
-			->setMailSend((bool)$data['mail_send'])
880
+			->setMailSend((bool) $data['mail_send'])
881 881
 			->setToken($data['token']);
882 882
 
883 883
 		$shareTime = new \DateTime();
884
-		$shareTime->setTimestamp((int)$data['stime']);
884
+		$shareTime->setTimestamp((int) $data['stime']);
885 885
 		$share->setShareTime($shareTime);
886 886
 		$share->setSharedWith($data['share_with']);
887 887
 
@@ -891,13 +891,13 @@  discard block
 block discarded – undo
891 891
 		} else {
892 892
 			//OLD SHARE
893 893
 			$share->setSharedBy($data['uid_owner']);
894
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
894
+			$path = $this->getNode($share->getSharedBy(), (int) $data['file_source']);
895 895
 
896 896
 			$owner = $path->getOwner();
897 897
 			$share->setShareOwner($owner->getUID());
898 898
 		}
899 899
 
900
-		$share->setNodeId((int)$data['file_source']);
900
+		$share->setNodeId((int) $data['file_source']);
901 901
 		$share->setNodeType($data['item_type']);
902 902
 
903 903
 		$share->setProviderId($this->identifier());
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.