Completed
Push — master ( 0f1d39...28c19e )
by Joas
13s
created

Item::__construct()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4.0218

Importance

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