Completed
Push — master ( b732ba...adfdb7 )
by
unknown
03:53
created

Item::__construct()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 4.0186

Importance

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