1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright (c) 2015 Victor Dubiniuk <[email protected]> |
4
|
|
|
* This file is licensed under the Affero General Public License version 3 or |
5
|
|
|
* later. |
6
|
|
|
* See the COPYING-README file. |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace OCA\Files_Antivirus; |
10
|
|
|
|
11
|
|
|
use OCA\Files_Antivirus\Activity\Provider; |
12
|
|
|
use OCA\Files_Antivirus\AppInfo\Application; |
13
|
|
|
use OCA\Files_Antivirus\Db\ItemMapper; |
14
|
|
|
use OCP\Activity\IManager as ActivityManager; |
15
|
|
|
use OCP\App; |
16
|
|
|
use OCP\AppFramework\Db\DoesNotExistException; |
17
|
|
|
use OCP\Files\File; |
18
|
|
|
use OCP\Files\IRootFolder; |
19
|
|
|
use OCA\Files_Trashbin\Trash\ITrashManager; |
20
|
|
|
use OCP\ILogger; |
21
|
|
|
|
22
|
|
|
class Item { |
23
|
|
|
/** |
24
|
|
|
* file handle, user to read from the file |
25
|
|
|
* @var resource |
26
|
|
|
*/ |
27
|
|
|
protected $fileHandle; |
28
|
|
|
|
29
|
|
|
/** @var AppConfig */ |
30
|
|
|
private $config; |
31
|
|
|
|
32
|
|
|
/** @var ActivityManager */ |
33
|
|
|
private $activityManager; |
34
|
|
|
|
35
|
|
|
/** @var ItemMapper */ |
36
|
|
|
private $itemMapper; |
37
|
|
|
|
38
|
|
|
/** @var ILogger */ |
39
|
|
|
private $logger; |
40
|
|
|
|
41
|
|
|
/** @var IRootFolder */ |
42
|
|
|
private $rootFolder; |
43
|
|
|
|
44
|
|
|
/** @var File */ |
45
|
|
|
private $file; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Item constructor. |
49
|
|
|
* |
50
|
|
|
* @param AppConfig $appConfig |
51
|
|
|
* @param ActivityManager $activityManager |
52
|
|
|
* @param ItemMapper $itemMapper |
53
|
|
|
* @param ILogger $logger |
54
|
|
|
* @param IRootFolder $rootFolder |
55
|
|
|
* @param File $file |
56
|
|
|
*/ |
57
|
|
|
public function __construct(AppConfig $appConfig, |
58
|
|
|
ActivityManager $activityManager, |
59
|
|
|
ItemMapper $itemMapper, |
60
|
|
|
ILogger $logger, |
61
|
|
|
IRootFolder $rootFolder, |
62
|
|
|
File $file) { |
63
|
|
|
$this->config = $appConfig; |
64
|
|
|
$this->activityManager = $activityManager; |
65
|
|
|
$this->itemMapper = $itemMapper; |
66
|
|
|
$this->logger = $logger; |
67
|
|
|
$this->rootFolder = $rootFolder; |
68
|
|
|
$this->file = $file; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Reads a file portion by portion until the very end |
73
|
|
|
* @return string|boolean |
74
|
|
|
*/ |
75
|
|
|
public function fread() { |
76
|
|
|
if (!($this->file->getSize() > 0)) { |
77
|
|
|
return false; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
if (is_null($this->fileHandle)) { |
81
|
|
|
$this->getFileHandle(); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
if (!is_null($this->fileHandle) && !$this->feof()) { |
85
|
|
|
return fread($this->fileHandle, $this->config->getAvChunkSize()); |
86
|
|
|
} |
87
|
|
|
return false; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Action to take if this item is infected |
92
|
|
|
*/ |
93
|
|
|
public function processInfected(Status $status) { |
94
|
|
|
$infectedAction = $this->config->getAvInfectedAction(); |
95
|
|
|
|
96
|
|
|
$shouldDelete = $infectedAction === 'delete'; |
97
|
|
|
|
98
|
|
|
$message = $shouldDelete ? Provider::MESSAGE_FILE_DELETED : ''; |
99
|
|
|
|
100
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->file->getOwner()->getUID()); |
101
|
|
|
$path = $userFolder->getRelativePath($this->file->getPath()); |
102
|
|
|
|
103
|
|
|
$activity = $this->activityManager->generateEvent(); |
104
|
|
|
$activity->setApp(Application::APP_NAME) |
105
|
|
|
->setSubject(Provider::SUBJECT_VIRUS_DETECTED_SCAN, [$status->getDetails()]) |
106
|
|
|
->setMessage($message) |
107
|
|
|
->setObject('file', $this->file->getId(), $path) |
108
|
|
|
->setAffectedUser($this->file->getOwner()->getUID()) |
109
|
|
|
->setType(Provider::TYPE_VIRUS_DETECTED); |
110
|
|
|
$this->activityManager->publish($activity); |
111
|
|
|
|
112
|
|
|
if ($shouldDelete) { |
113
|
|
|
$this->logError('Infected file deleted. ' . $status->getDetails()); |
114
|
|
|
$this->deleteFile(); |
115
|
|
|
} else { |
116
|
|
|
$this->logError('File is infected. ' . $status->getDetails()); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Action to take if this item status is unclear |
122
|
|
|
* @param Status $status |
123
|
|
|
*/ |
124
|
|
|
public function processUnchecked(Status $status) { |
125
|
|
|
//TODO: Show warning to the user: The file can not be checked |
126
|
|
|
$this->logError('Not Checked. ' . $status->getDetails()); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Action to take if this item status is not infected |
131
|
|
|
*/ |
132
|
|
|
public function processClean() { |
133
|
|
|
try { |
134
|
|
|
try { |
135
|
|
|
$item = $this->itemMapper->findByFileId($this->file->getId()); |
136
|
|
|
$this->itemMapper->delete($item); |
137
|
|
|
} catch (DoesNotExistException $e) { |
|
|
|
|
138
|
|
|
//Just ignore |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
$item = new \OCA\Files_Antivirus\Db\Item(); |
142
|
|
|
$item->setFileid($this->file->getId()); |
143
|
|
|
$item->setCheckTime(time()); |
144
|
|
|
$this->itemMapper->insert($item); |
|
|
|
|
145
|
|
|
} catch(\Exception $e) { |
146
|
|
|
$this->logger->error(__METHOD__.', exception: '.$e->getMessage(), ['app' => 'files_antivirus']); |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Check if the end of file is reached |
152
|
|
|
* @return boolean |
153
|
|
|
*/ |
154
|
|
|
private function feof() { |
155
|
|
|
$isDone = feof($this->fileHandle); |
156
|
|
|
if ($isDone) { |
157
|
|
|
$this->logDebug('Scan is done'); |
158
|
|
|
fclose($this->fileHandle); |
159
|
|
|
$this->fileHandle = null; |
160
|
|
|
} |
161
|
|
|
return $isDone; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Opens a file for reading |
166
|
|
|
* @throws \RuntimeException |
167
|
|
|
*/ |
168
|
|
|
private function getFileHandle() { |
169
|
|
|
$fileHandle = $this->file->fopen('r'); |
170
|
|
|
if ($fileHandle === false) { |
171
|
|
|
$this->logError('Can not open for reading.'); |
172
|
|
|
throw new \RuntimeException(); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$this->logDebug('Scan started'); |
176
|
|
|
$this->fileHandle = $fileHandle; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Delete infected file |
181
|
|
|
*/ |
182
|
|
|
private function deleteFile() { |
183
|
|
|
//prevent from going to trashbin |
184
|
|
|
if (App::isEnabled('files_trashbin')) { |
185
|
|
|
/** @var ITrashManager $trashManager */ |
186
|
|
|
$trashManager = \OC::$server->query(ITrashManager::class); |
187
|
|
|
$trashManager->pauseTrash(); |
188
|
|
|
} |
189
|
|
|
$this->file->delete(); |
190
|
|
|
if (App::isEnabled('files_trashbin')) { |
191
|
|
|
/** @var ITrashManager $trashManager */ |
192
|
|
|
$trashManager = \OC::$server->query(ITrashManager::class); |
193
|
|
|
$trashManager->resumeTrash(); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
private function generateExtraInfo() { |
198
|
|
|
$owner = $this->file->getOwner(); |
199
|
|
|
|
200
|
|
|
if ($owner === null) { |
201
|
|
|
$ownerInfo = 'Account: NO OWNER FOUND'; |
202
|
|
|
} else { |
203
|
|
|
$ownerInfo = 'Account: ' . $owner->getUID(); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
$extra = ' File: ' . $this->file->getId() |
207
|
|
|
. $ownerInfo |
208
|
|
|
. ' Path: ' . $this->file->getPath(); |
209
|
|
|
|
210
|
|
|
return $extra; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* @param string $message |
215
|
|
|
*/ |
216
|
|
|
public function logDebug($message) { |
217
|
|
|
$this->logger->debug($message . $this->generateExtraInfo(), ['app' => 'files_antivirus']); |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @param string $message |
222
|
|
|
*/ |
223
|
|
|
public function logError($message) { |
224
|
|
|
$this->logger->error($message . $this->generateExtraInfo(), ['app' => 'files_antivirus']); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
Scrutinizer analyzes your
composer.json
/composer.lock
file if available to determine the classes, and functions that are defined by your dependencies.It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.