Completed
Push — master ( 0d6f9c...3d08a4 )
by Roeland
11s
created

Item::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 2
crap 2
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\IL10N;
19
use OCP\ILogger;
20
21
class Item implements IScannable{
22
	/**
23
	 * file handle, user to read from the file
24
	 * @var resource
25
	 */
26
	protected $fileHandle;
27
28
	/** @var IL10N */
29
	private $l10n;
30
31
	/** @var AppConfig */
32
	private $config;
33
34
	/** @var ActivityManager */
35
	private $activityManager;
36
37
	/** @var ItemMapper */
38
	private $itemMapper;
39
40
	/** @var ILogger */
41
	private $logger;
42
43
	/** @var File */
44
	private $file;
45
46
	public function __construct(IL10N $l10n, File $file) {
47
		$this->l10n = $l10n;
48
		$this->file = $file;
49
50
		$application = new AppInfo\Application();
51
		$this->config = $application->getContainer()->query(AppConfig::class);
52
		$this->activityManager = \OC::$server->getActivityManager();
53
		$this->itemMapper = $application->getContainer()->query(ItemMapper::class);
54
		$this->logger = \OC::$server->getLogger();
55
	}
56
	
57
	/**
58
	 * Is this file good for scanning? 
59
	 * @return boolean
60
	 */
61
	public function isValid() {
62
		return $this->file->getSize() > 0;
63
	}
64
	
65
	/**
66
	 * Reads a file portion by portion until the very end
67
	 * @return string|boolean
68
	 */
69
	public function fread() {
70
		if (!$this->isValid()) {
71
			return false;
72
		}
73
		if (is_null($this->fileHandle)) {
74
			$this->getFileHandle();
75
		}
76
		
77
		if (!is_null($this->fileHandle) && !$this->feof()) {
78
			return fread($this->fileHandle, $this->config->getAvChunkSize());
79
		}
80
		return false;
81
	}
82
	
83
	/**
84
	 * Action to take if this item is infected
85
	 * @param Status $status
86
	 * @param boolean $isBackground
87
	 */
88
	public function processInfected(Status $status, $isBackground) {
89
		$infectedAction = $this->config->getAvInfectedAction();
90
		
91
		$shouldDelete = !$isBackground || ($isBackground && $infectedAction === 'delete');
92
		
93
		$message = $shouldDelete ? Provider::MESSAGE_FILE_DELETED : '';
94
95
		$activity = $this->activityManager->generateEvent();
96
		$activity->setApp(Application::APP_NAME)
97
			->setSubject(Provider::SUBJECT_VIRUS_DETECTED, [$this->file->getPath(), $status->getDetails()])
98
			->setMessage($message)
99
			->setObject('file', $this->file->getId(), $this->file->getPath())
100
			->setAffectedUser($this->file->getOwner()->getUID())
101
			->setType(Provider::TYPE_VIRUS_DETECTED);
102
		$this->activityManager->publish($activity);
103
104
		if ($isBackground) {
105
			if ($shouldDelete) {
106
				$this->logError('Infected file deleted. ' . $status->getDetails());
107
				$this->deleteFile();
108
			} else {
109
				$this->logError('File is infected. '  . $status->getDetails());
110
			}
111
		} else {
112
			$this->logError('Virus(es) found: ' . $status->getDetails());
113
			//remove file
114
			$this->deleteFile();
115
			Notification::sendMail($this->file->getPath());
116
			$message = $this->l10n->t(
117
						"Virus detected! Can't upload the file %s", 
118
						[$this->file->getName()]
119
			);
120
			\OCP\JSON::error(['data' => ['message' => $message]]);
121
			exit();
122
		}
123
	}
124
125
	/**
126
	 * Action to take if this item status is unclear
127
	 * @param Status $status
128
	 * @param boolean $isBackground
129
	 */
130
	public function processUnchecked(Status $status, $isBackground) {
0 ignored issues
show
Unused Code introduced by
The parameter $isBackground is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
131
		//TODO: Show warning to the user: The file can not be checked
132
		$this->logError('Not Checked. ' . $status->getDetails());
133
	}
134
	
135
	/**
136
	 * Action to take if this item status is not infected
137
	 * @param Status $status
138
	 * @param boolean $isBackground
139
	 */
140
	public function processClean(Status $status, $isBackground) {
0 ignored issues
show
Unused Code introduced by
The parameter $status is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
141
		if (!$isBackground) {
142
			return;
143
		}
144
		try {
145
			try {
146
				$item = $this->itemMapper->findByFileId($this->file->getId());
147
				$this->itemMapper->delete($item);
148
			} catch (DoesNotExistException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\AppFramework\Db\DoesNotExistException does not exist. Did you forget a USE statement, or did you not list all dependencies?

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.

Loading history...
149
				//Just ignore
150
			}
151
152
			$item = new \OCA\Files_Antivirus\Db\Item();
153
			$item->setFileid($this->file->getId());
154
			$item->setCheckTime(time());
155
			$this->itemMapper->insert($item);
156
		} catch(\Exception $e) {
157
			$this->logger->error(__METHOD__.', exception: '.$e->getMessage(), ['app' => 'files_antivirus']);
158
		}
159
	}
160
161
	/**
162
	 * Check if the end of file is reached
163
	 * @return boolean
164
	 */
165
	private function feof() {
166
		$isDone = feof($this->fileHandle);
167
		if ($isDone) {
168
			$this->logDebug('Scan is done');
169
			fclose($this->fileHandle);
170
			$this->fileHandle = null;
171
		}
172
		return $isDone;
173
	}
174
	
175
	/**
176
	 * Opens a file for reading
177
	 * @throws \RuntimeException
178
	 */
179
	private function getFileHandle() {
180
		$fileHandle = $this->file->fopen('r');
181
		if ($fileHandle === false) {
182
			$this->logError('Can not open for reading.');
183
			throw new \RuntimeException();
184
		}
185
186
		$this->logDebug('Scan started');
187
		$this->fileHandle = $fileHandle;
188
	}
189
190
	/**
191
	 * Delete infected file
192
	 */
193
	private function deleteFile() {
194
		//prevent from going to trashbin
195
		if (App::isEnabled('files_trashbin')) {
196
			\OCA\Files_Trashbin\Storage::preRenameHook([]);
197
		}
198
		$this->file->delete();
199
		if (App::isEnabled('files_trashbin')) {
200
			\OCA\Files_Trashbin\Storage::postRenameHook([]);
201
		}
202
	}
203
	
204
	/**
205
	 * @param string $message
206
	 */
207
	public function logDebug($message) {
208
		$extra = ' File: ' . $this->file->getId()
209
				. 'Account: ' . $this->file->getOwner()->getUID()
210
				. ' Path: ' . $this->file->getPath();
211
		$this->logger->debug($message . $extra, ['app' => 'files_antivirus']);
212
	}
213
	
214
	/**
215
	 * @param string $message
216
	 */
217
	public function logError($message) {
218
		$ownerInfo = 'Account: ' . $this->file->getOwner()->getUID();
219
		$extra = ' File: ' . $this->file->getId()
220
				. $ownerInfo 
221
				. ' Path: ' . $this->file->getPath();
222
		$this->logger->error($message . $extra, ['app' => 'files_antivirus']);
223
	}
224
}
225