Item::getFileHandle()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 8
cp 0.75
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2.0625
1
<?php
2
/**
3
 * ownCloud - files_antivirus
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Viktar Dubiniuk <[email protected]>
9
 *
10
 * @copyright Viktar Dubiniuk 2015-2018
11
 * @license AGPL-3.0
12
 */
13
14
namespace OCA\Files_Antivirus;
15
16
use OCP\IL10N;
17
use OCA\Files_Antivirus\Status;
18
use OCA\Files_Antivirus\Activity;
19
20
class Item implements IScannable {
21
	/**
22
	 * Scanned fileid (optional)
23
	 *
24
	 * @var int
25
	 */
26
	protected $id;
27
	
28
	/**
29
	 * File view
30
	 *
31
	 * @var \OC\Files\View
32
	 */
33
	protected $view;
34
	
35
	/**
36
	 * Path relative to the view
37
	 *
38
	 * @var string
39
	 */
40
	protected $path;
41
	
42
	/**
43
	 * file handle, user to read from the file
44
	 *
45
	 * @var resource
46
	 */
47
	protected $fileHandle;
48
	
49
	/**
50
	 * Portion size
51
	 *
52
	 * @var int
53
	 */
54
	protected $chunkSize;
55
	
56
	/**
57
	 * Is filesize match the size conditions
58
	 *
59
	 * @var bool
60
	 */
61
	protected $isValidSize;
62
	
63
	/**
64
	 * @var IL10N
65
	 */
66
	private $l10n;
67
	
68 5
	public function __construct(IL10N $l10n, $view, $path, $id = null) {
69 5
		$this->l10n = $l10n;
70
		
71 5
		if (!\is_object($view)) {
72
			$this->logError('Can\'t init filesystem view.', $id, $path);
73
			throw new \RuntimeException();
74
		}
75
		
76 5
		if (!$view->file_exists($path)) {
77 1
			$this->logError('File does not exist.', $id, $path);
78 1
			throw new \RuntimeException();
79
		}
80
		
81 5
		if ($id === null) {
82 1
			$this->id = $view->getFileInfo($path)->getId();
83
		} else {
84 4
			$this->id = $id;
85
		}
86
		
87 5
		$this->view = $view;
88 5
		$this->path = $path;
89
		
90 5
		$this->isValidSize = $view->filesize($path) > 0;
91
		
92 5
		$application = new AppInfo\Application();
93 5
		$config = $application->getContainer()->query('AppConfig');
94 5
		$this->chunkSize = $config->getAvChunkSize();
95 5
	}
96
	
97
	/**
98
	 * Is this file good for scanning?
99
	 *
100
	 * @return boolean
101
	 */
102 3
	public function isValid() {
103 3
		$isValid = !$this->view->is_dir($this->path) && $this->isValidSize;
104 3
		return $isValid;
105
	}
106
	
107
	/**
108
	 * Reads a file portion by portion until the very end
109
	 *
110
	 * @return string|boolean
111
	 */
112 3
	public function fread() {
113 3
		if (!$this->isValid()) {
114
			return;
115
		}
116 3
		if ($this->fileHandle === null) {
117 3
			$this->getFileHandle();
118
		}
119
		
120 3
		if ($this->fileHandle !== null && !$this->feof()) {
121 3
			$chunk = \fread($this->fileHandle, $this->chunkSize);
122 3
			return $chunk;
123
		}
124 2
		return false;
125
	}
126
	
127
	/**
128
	 * Action to take if this item is infected
129
	 *
130
	 * @param Status $status
131
	 * @param boolean $isBackground
132
	 */
133
	public function processInfected(Status $status, $isBackground) {
134
		$application = new AppInfo\Application();
135
		$appConfig = $application->getContainer()->query('AppConfig');
136
		$infectedAction = $appConfig->getAvInfectedAction();
137
		
138
		$shouldDelete = !$isBackground || ($isBackground && $infectedAction === 'delete');
139
		
140
		$message = $shouldDelete ? Activity::MESSAGE_FILE_DELETED : '';
141
		
142
		\OC::$server->getActivityManager()->publishActivity(
143
			'files_antivirus',
144
			Activity::SUBJECT_VIRUS_DETECTED,
145
			[$this->path, $status->getDetails()],
146
			$message,
147
			[],
148
			$this->path,
149
			'',
150
			$this->view->getOwner($this->path),
151
			Activity::TYPE_VIRUS_DETECTED,
152
			Activity::PRIORITY_HIGH
153
		);
154
		if ($isBackground) {
155
			if ($shouldDelete) {
156
				$this->logError('Infected file deleted. ' . $status->getDetails());
157
				$this->view->unlink($this->path);
158
			} else {
159
				$this->logError('File is infected. ' . $status->getDetails());
160
			}
161
		} else {
162
			$this->logError('Virus(es) found: ' . $status->getDetails());
163
			//remove file
164
			$this->view->unlink($this->path);
165
			Notification::sendMail($this->path);
166
			$message = $this->l10n->t(
167
				"Virus detected! Can't upload the file %s",
168
				[\basename($this->path)]
169
			);
170
			\OCP\JSON::error(["data" => ["message" => $message]]);
171
			exit();
172
		}
173
	}
174
175
	/**
176
	 * Action to take if this item status is unclear
177
	 *
178
	 * @param Status $status
179
	 * @param boolean $isBackground
180
	 */
181
	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...
182
		//TODO: Show warning to the user: The file can not be checked
183
		$this->logError('Not Checked. ' . $status->getDetails());
184
	}
185
	
186
	/**
187
	 * Action to take if this item status is not infected
188
	 *
189
	 * @param Status $status
190
	 * @param boolean $isBackground
191
	 */
192
	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...
193
		if (!$isBackground) {
194
			return;
195
		}
196
		try {
197
			$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*files_antivirus` WHERE `fileid` = ?');
198
			$result = $stmt->execute([$this->id]);
199
			if (\OCP\DB::isError($result)) {
200
				//TODO: Use logger
201
				$this->logError(__METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage());
202
			}
203
			$stmt = \OCP\DB::prepare(
204
				'INSERT INTO `*PREFIX*files_antivirus` (`fileid`, `check_time`) VALUES (?, ?)'
205
			);
206
			$result = $stmt->execute([$this->id, \time()]);
207
			if (\OCP\DB::isError($result)) {
208
				$this->logError(__METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage());
209
			}
210
		} catch (\Exception $e) {
211
			\OCP\Util::writeLog(
212
				'files_antivirus',
213
				__METHOD__ . ', exception: ' . $e->getMessage(),
214
				\OCP\Util::ERROR
215
			);
216
		}
217
	}
218
219
	/**
220
	 * Check if the end of file is reached
221
	 *
222
	 * @return boolean
223
	 */
224 3
	private function feof() {
225 3
		$isDone = \feof($this->fileHandle);
226 3
		if ($isDone) {
227 2
			$this->logDebug('Scan is done');
228 2
			\fclose($this->fileHandle);
229 2
			$this->fileHandle = null;
230
		}
231 3
		return $isDone;
232
	}
233
	
234
	/**
235
	 * Opens a file for reading
236
	 *
237
	 * @throws \RuntimeException
238
	 */
239 3
	private function getFileHandle() {
240 3
		$fileHandle = $this->view->fopen($this->path, "r");
241 3
		if ($fileHandle === false) {
242
			$this->logError('Can not open for reading.', $this->id, $this->path);
243
			throw new \RuntimeException();
244
		} else {
245 3
			$this->logDebug('Scan started');
246 3
			$this->fileHandle = $fileHandle;
247
		}
248 3
	}
249
	
250
	/**
251
	 * @param string $message
252
	 */
253 3
	public function logDebug($message) {
254 3
		$extra = ' File: ' . $this->id
255 3
				. ' Account: ' . $this->view->getOwner($this->path)
256 3
				. ' Path: ' . $this->path;
257 3
		\OCP\Util::writeLog('files_antivirus', $message . $extra, \OCP\Util::DEBUG);
258 3
	}
259
	
260
	/**
261
	 * @param string $message
262
	 * @param int $id optional
263
	 * @param string $path optional
264
	 */
265 1
	public function logError($message, $id=null, $path=null) {
266 1
		$ownerInfo = $this->view === null ? '' : ' Account: ' . $this->view->getOwner($path);
267 1
		$extra = ' File: ' . ($id === null ? $this->id : $id)
268 1
				. $ownerInfo
269 1
				. ' Path: ' . ($path === null ? $this->path : $path);
270 1
		\OCP\Util::writeLog(
271 1
			'files_antivirus',
272 1
			$message . $extra,
273 1
			\OCP\Util::ERROR
274
		);
275 1
	}
276
}
277