Completed
Push — add-is-scannable-size ( 4c2ec8 )
by Jörn Friedrich
07:57
created

AvirWrapper   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 7
Bugs 0 Features 1
Metric Value
wmc 17
lcom 1
cbo 3
dl 0
loc 151
rs 10
c 7
b 0
f 1

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
C fopen() 0 67 8
A isWritingMode() 0 9 1
B isScannableSize() 0 17 7
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 OC\Files\Filesystem;
12
use OC\Files\Storage\Wrapper\Wrapper;
13
use \OCP\App;
14
use \OCP\IL10N;
15
use \OCP\ILogger;
16
use \OCP\Files\InvalidContentException;
17
use Icewind\Streams\CallbackWrapper;
18
19
20
class AvirWrapper extends Wrapper{
21
	
22
	/**
23
	 * Modes that are used for writing 
24
	 * @var array 
25
	 */
26
	private $writingModes = array('r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+');
27
28
	/**
29
	 * @var AppConfig
30
	 */
31
	protected $appConfig;
32
33
	/**
34
	 * @var \OCA\Files_Antivirus\ScannerFactory
35
	 */
36
	protected $scannerFactory;
37
	
38
	/**
39
	 * @var IL10N 
40
	 */
41
	protected $l10n;
42
	
43
	/**
44
	 * @var ILogger;
45
	 */
46
	protected $logger;
47
48
	/**
49
	 * @param array $parameters
50
	 */
51
	public function __construct($parameters) {
52
		parent::__construct($parameters);
53
		$this->appConfig = $parameters['appConfig'];
54
		$this->scannerFactory = $parameters['scannerFactory'];
55
		$this->l10n = $parameters['l10n'];
56
		$this->logger = $parameters['logger'];
57
	}
58
	
59
	/**
60
	 * Asynchronously scan data that are written to the file
61
	 * @param string $path
62
	 * @param string $mode
63
	 * @return resource | bool
64
	 */
65
	public function fopen($path, $mode){
66
		$stream = $this->storage->fopen($path, $mode);
67
		if (is_resource($stream)
68
			&& $this->isWritingMode($mode)
69
			&& $this->isScannableSize(basename($path))
70
		) {
71
			try {
72
				$scanner = $this->scannerFactory->getScanner();
73
				$scanner->initScanner();
74
				return CallBackWrapper::wrap(
75
					$stream,
76
					null,
77
					function ($data) use ($scanner){
78
						$scanner->onAsyncData($data);
79
					}, 
80
					function () use ($scanner, $path) {
81
						$status = $scanner->completeAsyncScan();
82
						if (intval($status->getNumericStatus()) === \OCA\Files_Antivirus\Status::SCANRESULT_INFECTED){
83
							//prevent from going to trashbin
84
							if (App::isEnabled('files_trashbin')) {
85
								\OCA\Files_Trashbin\Storage::preRenameHook([
86
									Filesystem::signal_param_oldpath => '',
87
									Filesystem::signal_param_newpath => ''
88
								]);
89
							}
90
							
91
							$owner = $this->getOwner($path);
92
							$this->unlink($path);
93
94
							if (App::isEnabled('files_trashbin')) {
95
								\OCA\Files_Trashbin\Storage::postRenameHook([]);
96
							}
97
							$this->logger->warning(
98
								'Infected file deleted. ' . $status->getDetails()
99
								. ' Account: ' . $owner . ' Path: ' . $path,
100
								['app' => 'files_antivirus']
101
							);
102
103
							\OC::$server->getActivityManager()->publishActivity(
104
								'files_antivirus',
105
								Activity::SUBJECT_VIRUS_DETECTED,
106
								[$path, $status->getDetails()],
107
								Activity::MESSAGE_FILE_DELETED,
108
								[],
109
								$path,
110
								'',
111
								$owner,
112
								Activity::TYPE_VIRUS_DETECTED,
113
								Activity::PRIORITY_HIGH
114
							);
115
											
116
							throw new InvalidContentException(
117
								$this->l10n->t(
118
									'Virus %s is detected in the file. Upload cannot be completed.',
119
									$status->getDetails()
120
								)
121
							);
122
						}
123
					}
124
				);
125
			} catch (\Exception $e){
126
				$message = 	implode(' ', [ __CLASS__, __METHOD__, $e->getMessage()]);
127
				$this->logger->warning($message);
128
			}
129
		}
130
		return $stream;
131
	}
132
	
133
	/**
134
	 * Checks whether passed mode is suitable for writing 
135
	 * @param string $mode
136
	 * @return bool
137
	 */
138
	private function isWritingMode($mode){
139
		// Strip unessential binary/text flags
140
		$cleanMode = str_replace(
141
			['t', 'b'],
142
			['', ''],
143
			$mode
144
		);
145
		return in_array($cleanMode, $this->writingModes);
146
	}
147
148
	/**
149
	 * checks the size for POST and webdav PUT requests. defaults to true
150
	 * @param $filename
151
	 * @return bool
152
	 */
153
	private function isScannableSize($filename) {
154
		$scanSizeLimit = intval($this->appConfig->getAvMaxFileSize());
155
		$size = false;
156
157
		// POST on upload.php
158
		if (isset($_FILES['files']['name'])) {
159
			$keys = array_keys($_FILES['files']['name'], $filename, true);
160
			if (count($keys) === 1) {
161
				$size = $_FILES['files']['size'][$keys[0]];
162
			}
163
		// PUT via webdav
164
		} else if ($_SERVER['REQUEST_METHOD'] === 'PUT' && isset($_SERVER['CONTENT_LENGTH'])) {
165
			$size = $_SERVER['CONTENT_LENGTH'];
166
		}
167
168
		return $scanSizeLimit === -1 || $size === false || $scanSizeLimit >= $size;
169
	}
170
}
171