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