Completed
Push — master ( a6348a...e1ed4c )
by Morris
06:14
created

Item::deleteFile()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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