This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * @copyright Copyright (c) 2016, ownCloud, Inc. |
||
4 | * |
||
5 | * @author Frank Karlitschek <[email protected]> |
||
6 | * @author Joas Schilling <[email protected]> |
||
7 | * @author Thomas Müller <[email protected]> |
||
8 | * |
||
9 | * @license AGPL-3.0 |
||
10 | * |
||
11 | * This code is free software: you can redistribute it and/or modify |
||
12 | * it under the terms of the GNU Affero General Public License, version 3, |
||
13 | * as published by the Free Software Foundation. |
||
14 | * |
||
15 | * This program is distributed in the hope that it will be useful, |
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | * GNU Affero General Public License for more details. |
||
19 | * |
||
20 | * You should have received a copy of the GNU Affero General Public License, version 3, |
||
21 | * along with this program. If not, see <http://www.gnu.org/licenses/> |
||
22 | * |
||
23 | */ |
||
24 | |||
25 | namespace OCA\Activity; |
||
26 | |||
27 | use OCP\Activity\IEvent; |
||
28 | use OCP\Activity\IExtension; |
||
29 | use OCP\Activity\IFilter; |
||
30 | use OCP\Activity\IManager; |
||
31 | use OCP\DB\QueryBuilder\IQueryBuilder; |
||
32 | use OCP\IDBConnection; |
||
33 | use OCP\IL10N; |
||
34 | |||
35 | /** |
||
36 | * @brief Class for managing the data in the activities |
||
37 | */ |
||
38 | class Data { |
||
39 | /** @var IManager */ |
||
40 | protected $activityManager; |
||
41 | |||
42 | /** @var IDBConnection */ |
||
43 | protected $connection; |
||
44 | |||
45 | /** |
||
46 | * @param IManager $activityManager |
||
47 | * @param IDBConnection $connection |
||
48 | */ |
||
49 | 40 | public function __construct(IManager $activityManager, IDBConnection $connection) { |
|
50 | 40 | $this->activityManager = $activityManager; |
|
51 | 40 | $this->connection = $connection; |
|
52 | 40 | } |
|
53 | |||
54 | /** |
||
55 | * Send an event into the activity stream |
||
56 | * |
||
57 | * @param IEvent $event |
||
58 | * @return bool |
||
59 | */ |
||
60 | 5 | public function send(IEvent $event) { |
|
61 | 5 | if ($event->getAffectedUser() === '' || $event->getAffectedUser() === null) { |
|
62 | 2 | return false; |
|
63 | } |
||
64 | |||
65 | // store in DB |
||
66 | 3 | $queryBuilder = $this->connection->getQueryBuilder(); |
|
67 | 3 | $queryBuilder->insert('activity') |
|
68 | 3 | ->values([ |
|
69 | 3 | 'app' => $queryBuilder->createParameter('app'), |
|
70 | 3 | 'subject' => $queryBuilder->createParameter('subject'), |
|
71 | 3 | 'subjectparams' => $queryBuilder->createParameter('subjectparams'), |
|
72 | 3 | 'message' => $queryBuilder->createParameter('message'), |
|
73 | 3 | 'messageparams' => $queryBuilder->createParameter('messageparams'), |
|
74 | 3 | 'file' => $queryBuilder->createParameter('object_name'), |
|
75 | 3 | 'link' => $queryBuilder->createParameter('link'), |
|
76 | 3 | 'user' => $queryBuilder->createParameter('user'), |
|
77 | 3 | 'affecteduser' => $queryBuilder->createParameter('affecteduser'), |
|
78 | 3 | 'timestamp' => $queryBuilder->createParameter('timestamp'), |
|
79 | 3 | 'priority' => $queryBuilder->createParameter('priority'), |
|
80 | 3 | 'type' => $queryBuilder->createParameter('type'), |
|
81 | 3 | 'object_type' => $queryBuilder->createParameter('object_type'), |
|
82 | 3 | 'object_id' => $queryBuilder->createParameter('object_id'), |
|
83 | ]) |
||
84 | 3 | ->setParameters([ |
|
85 | 3 | 'app' => $event->getApp(), |
|
86 | 3 | 'type' => $event->getType(), |
|
87 | 3 | 'affecteduser' => $event->getAffectedUser(), |
|
88 | 3 | 'user' => $event->getAuthor(), |
|
89 | 3 | 'timestamp' => (int) $event->getTimestamp(), |
|
90 | 3 | 'subject' => $event->getSubject(), |
|
91 | 3 | 'subjectparams' => json_encode($event->getSubjectParameters()), |
|
92 | 3 | 'message' => $event->getMessage(), |
|
93 | 3 | 'messageparams' => json_encode($event->getMessageParameters()), |
|
94 | 3 | 'priority' => IExtension::PRIORITY_MEDIUM, |
|
95 | 3 | 'object_type' => $event->getObjectType(), |
|
96 | 3 | 'object_id' => (int) $event->getObjectId(), |
|
97 | 3 | 'object_name' => $event->getObjectName(), |
|
98 | 3 | 'link' => $event->getLink(), |
|
99 | ]) |
||
100 | 3 | ->execute(); |
|
101 | |||
102 | 3 | return true; |
|
103 | } |
||
104 | |||
105 | /** |
||
106 | * Send an event as email |
||
107 | * |
||
108 | * @param IEvent $event |
||
109 | * @param int $latestSendTime Activity $timestamp + batch setting of $affectedUser |
||
110 | * @return bool |
||
111 | */ |
||
112 | 4 | public function storeMail(IEvent $event, int $latestSendTime): bool { |
|
113 | 4 | $affectedUser = $event->getAffectedUser(); |
|
114 | 4 | if ($affectedUser === '' || $affectedUser === null) { |
|
115 | 2 | return false; |
|
116 | } |
||
117 | |||
118 | 2 | $query = $this->connection->getQueryBuilder(); |
|
119 | 2 | $query->insert('activity_mq') |
|
120 | 2 | ->values([ |
|
121 | 2 | 'amq_appid' => $query->createNamedParameter($event->getApp()), |
|
122 | 2 | 'amq_subject' => $query->createNamedParameter($event->getSubject()), |
|
123 | 2 | 'amq_subjectparams' => $query->createNamedParameter(json_encode($event->getSubjectParameters())), |
|
124 | 2 | 'amq_affecteduser' => $query->createNamedParameter($affectedUser), |
|
125 | 2 | 'amq_timestamp' => $query->createNamedParameter((int) $event->getTimestamp()), |
|
126 | 2 | 'amq_type' => $query->createNamedParameter($event->getType()), |
|
127 | 2 | 'amq_latest_send' => $query->createNamedParameter($latestSendTime), |
|
128 | 2 | 'object_type' => $query->createNamedParameter($event->getObjectType()), |
|
129 | 2 | 'object_id' => $query->createNamedParameter($event->getObjectId()), |
|
130 | ]); |
||
131 | 2 | $query->execute(); |
|
132 | |||
133 | 2 | return true; |
|
134 | } |
||
135 | |||
136 | /** |
||
137 | * Read a list of events from the activity stream |
||
138 | * |
||
139 | * @param GroupHelper $groupHelper Allows activities to be grouped |
||
140 | * @param UserSettings $userSettings Gets the settings of the user |
||
141 | * @param string $user User for whom we display the stream |
||
142 | * |
||
143 | * @param int $since The integer ID of the last activity that has been seen. |
||
144 | * @param int $limit How many activities should be returned |
||
145 | * @param string $sort Should activities be given ascending or descending |
||
146 | * |
||
147 | * @param string $filter Filter the activities |
||
148 | * @param string $objectType Allows to filter the activities to a given object. May only appear together with $objectId |
||
149 | * @param int $objectId Allows to filter the activities to a given object. May only appear together with $objectType |
||
150 | * |
||
151 | * @return array |
||
152 | * |
||
153 | * @throws \OutOfBoundsException if the user (Code: 1) or the since (Code: 2) is invalid |
||
154 | * @throws \BadMethodCallException if the user has selected to display no types for this filter (Code: 3) |
||
155 | */ |
||
156 | 5 | public function get(GroupHelper $groupHelper, UserSettings $userSettings, $user, $since, $limit, $sort, $filter, $objectType = '', $objectId = 0) { |
|
157 | // get current user |
||
158 | 5 | if ($user === '') { |
|
159 | throw new \OutOfBoundsException('Invalid user', 1); |
||
160 | } |
||
161 | 5 | $groupHelper->setUser($user); |
|
162 | |||
163 | 5 | $activeFilter = null; |
|
164 | try { |
||
165 | 5 | $activeFilter = $this->activityManager->getFilterById($filter); |
|
166 | 5 | } catch (\InvalidArgumentException $e) { |
|
167 | // Unknown filter => ignore and show all activities |
||
168 | } |
||
169 | |||
170 | 5 | $enabledNotifications = $userSettings->getNotificationTypes($user, 'stream'); |
|
171 | 5 | if ($activeFilter instanceof IFilter) { |
|
0 ignored issues
–
show
|
|||
172 | $enabledNotifications = $activeFilter->filterTypes($enabledNotifications); |
||
173 | } |
||
174 | 5 | $enabledNotifications = array_unique($enabledNotifications); |
|
175 | |||
176 | // We don't want to display any activities |
||
177 | 5 | if (empty($enabledNotifications)) { |
|
178 | throw new \BadMethodCallException('No settings enabled', 3); |
||
179 | } |
||
180 | |||
181 | 5 | $query = $this->connection->getQueryBuilder(); |
|
182 | 5 | $query->select('*') |
|
183 | 5 | ->from('activity'); |
|
184 | |||
185 | 5 | $query->where($query->expr()->eq('affecteduser', $query->createNamedParameter($user))) |
|
186 | 5 | ->andWhere($query->expr()->in('type', $query->createNamedParameter($enabledNotifications, IQueryBuilder::PARAM_STR_ARRAY))); |
|
187 | 5 | if ($filter === 'self') { |
|
188 | $query->andWhere($query->expr()->eq('user', $query->createNamedParameter($user))); |
||
189 | |||
190 | 5 | } else if ($filter === 'by') { |
|
191 | $query->andWhere($query->expr()->neq('user', $query->createNamedParameter($user))); |
||
192 | |||
193 | 5 | } else if ($filter === 'all' && !$userSettings->getUserSetting($user, 'setting', 'self')) { |
|
194 | $query->andWhere($query->expr()->orX( |
||
195 | $query->expr()->neq('user', $query->createNamedParameter($user)), |
||
196 | $query->expr()->notIn('type', $query->createNamedParameter([ |
||
197 | 'file_created', |
||
198 | 'file_changed', |
||
199 | 'file_deleted', |
||
200 | 'file_restored', |
||
201 | ], IQueryBuilder::PARAM_STR_ARRAY)) |
||
202 | )); |
||
203 | |||
204 | 5 | } else if ($filter === 'filter') { |
|
205 | if (!$userSettings->getUserSetting($user, 'setting', 'self')) { |
||
206 | $query->andWhere($query->expr()->orX( |
||
207 | $query->expr()->neq('user', $query->createNamedParameter($user)), |
||
208 | $query->expr()->notIn('type', $query->createNamedParameter([ |
||
209 | 'file_created', |
||
210 | 'file_changed', |
||
211 | 'file_deleted', |
||
212 | 'file_restored', |
||
213 | ], IQueryBuilder::PARAM_STR_ARRAY)) |
||
214 | )); |
||
215 | } |
||
216 | |||
217 | $query->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType))); |
||
218 | $query->andWhere($query->expr()->eq('object_id', $query->createNamedParameter($objectId))); |
||
219 | } |
||
220 | |||
221 | 5 | if ($activeFilter instanceof IFilter) { |
|
0 ignored issues
–
show
The class
OCP\Activity\IFilter does not exist. Did you forget a USE statement, or did you not list all dependencies?
This error could be the result of: 1. Missing dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
222 | $apps = $activeFilter->allowedApps(); |
||
223 | if (!empty($apps)) { |
||
224 | $query->andWhere($query->expr()->in('app', $query->createNamedParameter($apps, IQueryBuilder::PARAM_STR_ARRAY))); |
||
225 | } |
||
226 | } |
||
227 | |||
228 | if ( |
||
229 | 5 | $filter === 'files_favorites' || |
|
230 | 5 | (in_array($filter, ['all', 'by', 'self']) && $userSettings->getUserSetting($user, 'stream', 'files_favorites')) |
|
231 | ) { |
||
232 | try { |
||
233 | $favoriteFilter = $this->activityManager->getFilterById('files_favorites'); |
||
234 | /** @var \OCA\Files\Activity\Filter\Favorites $favoriteFilter */ |
||
235 | $favoriteFilter->filterFavorites($query); |
||
236 | } catch (\InvalidArgumentException $e) { |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
|
|||
237 | } |
||
238 | } |
||
239 | |||
240 | /** |
||
241 | * Order and specify the offset |
||
242 | */ |
||
243 | 5 | $sqlSort = ($sort === 'asc') ? 'ASC' : 'DESC'; |
|
244 | 5 | $headers = $this->setOffsetFromSince($query, $user, $since, $sqlSort); |
|
245 | 5 | $query->orderBy('timestamp', $sqlSort) |
|
246 | 5 | ->addOrderBy('activity_id', $sqlSort); |
|
247 | |||
248 | 5 | $query->setMaxResults($limit + 1); |
|
249 | |||
250 | 5 | $result = $query->execute(); |
|
251 | 5 | $hasMore = false; |
|
252 | 5 | while ($row = $result->fetch()) { |
|
253 | 4 | if ($limit === 0) { |
|
254 | 2 | $hasMore = true; |
|
255 | 2 | break; |
|
256 | } |
||
257 | 4 | $headers['X-Activity-Last-Given'] = (int) $row['activity_id']; |
|
258 | 4 | $groupHelper->addActivity($row); |
|
259 | 4 | $limit--; |
|
260 | } |
||
261 | 5 | $result->closeCursor(); |
|
262 | |||
263 | 5 | return ['data' => $groupHelper->getActivities(), 'has_more' => $hasMore, 'headers' => $headers]; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * @param IQueryBuilder $query |
||
268 | * @param string $user |
||
269 | * @param int $since |
||
270 | * @param string $sort |
||
271 | * |
||
272 | * @return array Headers that should be set on the response |
||
273 | * |
||
274 | * @throws \OutOfBoundsException If $since is not owned by $user |
||
275 | */ |
||
276 | 10 | protected function setOffsetFromSince(IQueryBuilder $query, $user, $since, $sort) { |
|
277 | 10 | if ($since) { |
|
278 | 5 | $queryBuilder = $this->connection->getQueryBuilder(); |
|
279 | 5 | $queryBuilder->select(['affecteduser', 'timestamp']) |
|
280 | 5 | ->from('activity') |
|
281 | 5 | ->where($queryBuilder->expr()->eq('activity_id', $queryBuilder->createNamedParameter((int) $since))); |
|
282 | 5 | $result = $queryBuilder->execute(); |
|
283 | 5 | $activity = $result->fetch(); |
|
284 | 5 | $result->closeCursor(); |
|
285 | |||
286 | 5 | if ($activity) { |
|
287 | 4 | if ($activity['affecteduser'] !== $user) { |
|
288 | 1 | throw new \OutOfBoundsException('Invalid since', 2); |
|
289 | } |
||
290 | 3 | $timestamp = (int) $activity['timestamp']; |
|
291 | |||
292 | 3 | if ($sort === 'DESC') { |
|
293 | 2 | $query->andWhere($query->expr()->lte('timestamp', $query->createNamedParameter($timestamp))); |
|
294 | 2 | $query->andWhere($query->expr()->lt('activity_id', $query->createNamedParameter($since))); |
|
295 | } else { |
||
296 | 1 | $query->andWhere($query->expr()->gte('timestamp', $query->createNamedParameter($timestamp))); |
|
297 | 1 | $query->andWhere($query->expr()->gt('activity_id', $query->createNamedParameter($since))); |
|
298 | } |
||
299 | 3 | return []; |
|
300 | } |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * Couldn't find the since, so find the oldest one and set the header |
||
305 | */ |
||
306 | 6 | $fetchQuery = $this->connection->getQueryBuilder(); |
|
307 | 6 | $fetchQuery->select('activity_id') |
|
308 | 6 | ->from('activity') |
|
309 | 6 | ->where($fetchQuery->expr()->eq('affecteduser', $fetchQuery->createNamedParameter($user))) |
|
310 | 6 | ->orderBy('timestamp', $sort) |
|
311 | 6 | ->setMaxResults(1); |
|
312 | 6 | $result = $fetchQuery->execute(); |
|
313 | 6 | $activity = $result->fetch(); |
|
314 | 6 | $result->closeCursor(); |
|
315 | |||
316 | 6 | if ($activity !== false) { |
|
317 | return [ |
||
318 | 4 | 'X-Activity-First-Known' => (int) $activity['activity_id'], |
|
319 | ]; |
||
320 | } |
||
321 | |||
322 | 2 | return []; |
|
323 | } |
||
324 | |||
325 | /** |
||
326 | * Verify that the filter is valid |
||
327 | * |
||
328 | * @param string $filterValue |
||
329 | * @return string |
||
330 | */ |
||
331 | public function validateFilter($filterValue) { |
||
332 | if (!isset($filterValue)) { |
||
333 | return 'all'; |
||
334 | } |
||
335 | |||
336 | switch ($filterValue) { |
||
337 | case 'filter': |
||
338 | return $filterValue; |
||
339 | default: |
||
340 | try { |
||
341 | $this->activityManager->getFilterById($filterValue); |
||
342 | return $filterValue; |
||
343 | } catch (\InvalidArgumentException $e) { |
||
344 | return 'all'; |
||
345 | } |
||
346 | } |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Delete old events |
||
351 | * |
||
352 | * @param int $expireDays Minimum 1 day |
||
353 | */ |
||
354 | 1 | public function expire($expireDays = 365) { |
|
355 | 1 | $ttl = (60 * 60 * 24 * max(1, $expireDays)); |
|
356 | |||
357 | 1 | $timelimit = time() - $ttl; |
|
358 | 1 | $this->deleteActivities(array( |
|
359 | 1 | 'timestamp' => array($timelimit, '<'), |
|
360 | )); |
||
361 | 1 | } |
|
362 | |||
363 | /** |
||
364 | * Delete activities that match certain conditions |
||
365 | * |
||
366 | * @param array $conditions Array with conditions that have to be met |
||
367 | * 'field' => 'value' => `field` = 'value' |
||
368 | * 'field' => array('value', 'operator') => `field` operator 'value' |
||
369 | */ |
||
370 | 11 | public function deleteActivities($conditions) { |
|
371 | 11 | $sqlWhere = ''; |
|
372 | 11 | $sqlParameters = $sqlWhereList = array(); |
|
373 | 11 | foreach ($conditions as $column => $comparison) { |
|
374 | 11 | $sqlWhereList[] = " `$column` " . ((is_array($comparison) && isset($comparison[1])) ? $comparison[1] : '=') . ' ? '; |
|
375 | 11 | $sqlParameters[] = (is_array($comparison)) ? $comparison[0] : $comparison; |
|
376 | } |
||
377 | |||
378 | 11 | if (!empty($sqlWhereList)) { |
|
379 | 11 | $sqlWhere = ' WHERE ' . implode(' AND ', $sqlWhereList); |
|
380 | } |
||
381 | |||
382 | 11 | $query = $this->connection->prepare( |
|
383 | 11 | 'DELETE FROM `*PREFIX*activity`' . $sqlWhere); |
|
384 | 11 | $query->execute($sqlParameters); |
|
385 | 11 | } |
|
386 | } |
||
387 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.