Completed
Pull Request — master (#87)
by Roeland
26:41 queued 21:21
created

AvirWrapper   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 130
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 36.21%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 11
lcom 1
cbo 2
dl 0
loc 130
ccs 21
cts 58
cp 0.3621
rs 10
c 3
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B fopen() 0 73 9
A isWritingMode() 0 9 1
1
<?php
2
/**
3
 * Copyright (c) 2014 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 Icewind\Streams\CallbackWrapper;
12
use OC\Files\Storage\Wrapper\Jail;
13
use OC\Files\Storage\Wrapper\Wrapper;
14
use OCA\Files_Antivirus\Activity\Provider;
15
use OCA\Files_Antivirus\AppInfo\Application;
16
use OCA\Files_Antivirus\Scanner\ScannerFactory;
17
use OCP\Activity\IManager as ActivityManager;
18
use OCP\App;
19
use OCP\Files\InvalidContentException;
20
use OCP\IL10N;
21
use OCP\ILogger;
22
23
class AvirWrapper extends Wrapper{
24
	
25
	/**
26
	 * Modes that are used for writing 
27
	 * @var array 
28
	 */
29
	private $writingModes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'];
30
	
31
	/** @var ScannerFactory */
32
	protected $scannerFactory;
33
	
34
	/** @var IL10N */
35
	protected $l10n;
36
	
37
	/** @var ILogger */
38
	protected $logger;
39
40
	/** @var ActivityManager */
41
	protected $activityManager;
42
43
	/** @var bool */
44
	protected $isHomeStorage;
45
46
	/**
47
	 * @param array $parameters
48
	 */
49 2
	public function __construct($parameters) {
50 2
		parent::__construct($parameters);
51 2
		$this->scannerFactory = $parameters['scannerFactory'];
52 2
		$this->l10n = $parameters['l10n'];
53 2
		$this->logger = $parameters['logger'];
54 2
		$this->activityManager = $parameters['activityManager'];
55 2
		$this->isHomeStorage = $parameters['isHomeStorage'];
56 2
	}
57
	
58
	/**
59
	 * Asynchronously scan data that are written to the file
60
	 * @param string $path
61
	 * @param string $mode
62
	 * @return resource | bool
63
	 */
64 1
	public function fopen($path, $mode) {
65 1
		$stream = $this->storage->fopen($path, $mode);
66
67
		/*
68
		 * Only check when
69
		 *  - it is a resource
70
		 *  - it is a writing mode
71
		 *  - if it is a homestorage it starts with files/
72
		 *  - if it is not a homestorage we always wrap (external storages)
73
		 */
74 1
		if (is_resource($stream) && $this->isWritingMode($mode) && (!$this->isHomeStorage || strpos($path, 'files/') === 0)) {
75
			try {
76 1
				$scanner = $this->scannerFactory->getScanner();
77
				$scanner->initScanner();
78
				return CallbackWrapper::wrap(
79
					$stream,
80
					null,
81
					function ($data) use ($scanner){
82
						$scanner->onAsyncData($data);
83
					}, 
84
					function () use ($scanner, $path) {
85
						$status = $scanner->completeAsyncScan();
86
						if ((int)$status->getNumericStatus() === Status::SCANRESULT_INFECTED){
87
							//prevent from going to trashbin
88
							if (App::isEnabled('files_trashbin')) {
89
								\OCA\Files_Trashbin\Storage::preRenameHook([
90
									'oldpath' => '',
91
									'newpath' => ''
92
								]);
93
							}
94
							
95
							$owner = $this->getOwner($path);
96
							$this->unlink($path);
97
98
							if (App::isEnabled('files_trashbin')) {
99
								\OCA\Files_Trashbin\Storage::preRenameHook([
100
									'oldpath' => '',
101
									'newpath' => ''
102
								]);
103
							}
104
							$this->logger->warning(
105
								'Infected file deleted. ' . $status->getDetails()
106
								. ' Account: ' . $owner . ' Path: ' . $path,
107
								['app' => 'files_antivirus']
108
							);
109
110
							$activity = $this->activityManager->generateEvent();
111
							$activity->setApp(Application::APP_NAME)
112
								->setSubject(Provider::SUBJECT_VIRUS_DETECTED_UPLOAD, [$status->getDetails()])
113
								->setMessage(Provider::MESSAGE_FILE_DELETED)
114
								->setObject('', 0, $path)
115
								->setAffectedUser($owner)
116
								->setType(Provider::TYPE_VIRUS_DETECTED);
117
							$this->activityManager->publish($activity);
118
119
							$this->logger->error('Infected file deleted. ' . $status->getDetails() . 
120
							' File: ' . $path . ' Acccount: ' . $owner, ['app' => 'files_antivirus']);
121
122
							throw new InvalidContentException(
123
								$this->l10n->t(
124
									'Virus %s is detected in the file. Upload cannot be completed.',
125
									$status->getDetails()
126
								)
127
							);
128
						}
129
					}
130
				);
131 1
			} catch (\Exception $e){
132 1
				$this->logger->logException($e);
133
			}
134
		}
135 1
		return $stream;
136
	}
137
	
138
	/**
139
	 * Checks whether passed mode is suitable for writing 
140
	 * @param string $mode
141
	 * @return bool
142
	 */
143 1
	private function isWritingMode($mode){
144
		// Strip unessential binary/text flags
145 1
		$cleanMode = str_replace(
146 1
			['t', 'b'],
147 1
			['', ''],
148 1
			$mode
149
		);
150 1
		return in_array($cleanMode, $this->writingModes);
151
	}
152
}
153