Completed
Pull Request — master (#76)
by
unknown
01:42
created

ScannerBase::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
3
/**
4
* ownCloud - files_antivirus
5
*
6
* @author Manuel Deglado
7
* @copyright 2012 Manuel Deglado [email protected]
8
*
9
* This library is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
11
* License as published by the Free Software Foundation; either
12
* version 3 of the License, or any later version.
13
*
14
* This library is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
18
*
19
* You should have received a copy of the GNU Affero General Public
20
* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
21
*
22
*/
23
24
namespace OCA\Files_Antivirus\Scanner;
25
26
use OCA\Files_Antivirus\AppConfig;
27
use OCA\Files_Antivirus\Item;
28
use OCA\Files_Antivirus\Status;
29
use OCA\Files_Antivirus\StatusFactory;
30
use OCP\ILogger;
31
32
abstract class ScannerBase {
33
	
34
	/**
35
	 * Scan result
36
	 * @var Status
37
	 */
38
	protected $status;
39
40
	/**
41
	 * If scanning was done part by part
42
	 * the first detected infected part is stored here
43
	 * @var Status
44
	 */
45
	protected $infectedStatus;
46
47
	/** @var  int */
48
	protected $byteCount;
49
50
	/** @var  resource */
51
	protected $writeHandle;
52
53
	/** @var AppConfig */
54
	protected $appConfig;
55
56
	/** @var ILogger */
57
	protected $logger;
58
59
	/** @var StatusFactory */
60
	protected $statusFactory;
61
62
	/** @var string */
63
	protected $lastChunk;
64
65
	/** @var bool */
66
	protected $isLogUsed = false;
67
68
	/** @var bool */
69
	protected $isAborted = false;
70
71
	/**
72
	 * ScannerBase constructor.
73
	 *
74
	 * @param AppConfig $config
75
	 * @param ILogger $logger
76
	 * @param StatusFactory $statusFactory
77
	 */
78 2
	public function __construct(AppConfig $config, ILogger $logger, StatusFactory $statusFactory) {
79 2
		$this->appConfig = $config;
80 2
		$this->logger = $logger;
81 2
		$this->statusFactory = $statusFactory;
82 2
	}
83
84
	/**
85
	 * Close used resources
86
	 */
87
	abstract protected function shutdownScanner();
88
89
90
	public function getStatus(){
91
		if ($this->infectedStatus instanceof Status){
92
			return $this->infectedStatus;
93
		}
94
		if ($this->status instanceof Status){
95
			return $this->status;
96
		}
97
		return $this->statusFactory->newStatus();
98
	}
99
100
	/**
101
	 * Synchronous scan
102
	 * @param Item $item
103
	 * @return Status
104
	 */
105
	public function scan(Item $item) {
106
		$this->initScanner();
107
108
		while (false !== ($chunk = $item->fread())) {
109
			$this->writeChunk($chunk);
110
		}
111
		
112
		$this->shutdownScanner();
113
		return $this->getStatus();
114
	}
115
	
116
	/**
117
	 * Async scan - new portion of data is available
118
	 * @param string $data
119
	 */
120
	public function onAsyncData($data){
121
		$this->writeChunk($data);
122
	}
123
	
124
	/**
125
	 * Async scan - resource is closed
126
	 * @return Status
127
	 */
128
	public function completeAsyncScan(){
129
		$this->shutdownScanner();
130
		return $this->getStatus();
131
	}
132
	
133
	/**
134
	 * Open write handle. etc
135
	 */
136
	public function initScanner(){
137
		$this->byteCount = 0;
138
		if ($this->status instanceof Status && $this->status->getNumericStatus() === Status::SCANRESULT_INFECTED){
139
			$this->infectedStatus = clone $this->status;
140
		}
141
		$this->status = $this->statusFactory->newStatus();
142
	}
143
144
	/**
145
	 * @param string $chunk
146
	 */
147
	protected function writeChunk($chunk){
148
		$this->fwrite(
149
			$this->prepareChunk($chunk)
150
		);
151
	}
152
153
	/**
154
	 * @param string $data
155
	 */
156
	final protected function fwrite($data){
157
		if ($this->isAborted){
158
			return;
159
		}
160
161
		$dataLength = strlen($data);
162
		$streamSizeLimit = (int)$this->appConfig->getAvStreamMaxLength();
0 ignored issues
show
Documentation Bug introduced by
The method getAvStreamMaxLength does not exist on object<OCA\Files_Antivirus\AppConfig>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
163
		if ($this->byteCount + $dataLength > $streamSizeLimit){
164
			\OC::$server->getLogger()->debug(
165
				'reinit scanner',
166
				['app' => 'files_antivirus']
167
			);
168
			$this->shutdownScanner();
169
			$isReopenSuccessful = $this->retry();
170
		} else {
171
			$isReopenSuccessful = true;
172
		}
173
174
		if (!$isReopenSuccessful || !$this->writeRaw($data)){
175
			if (!$this->isLogUsed) {
176
				$this->isLogUsed = true;
177
				\OC::$server->getLogger()->warning(
178
					'Failed to write a chunk. Check if Stream Length matches StreamMaxLength in ClamAV daemon settings',
179
					['app' => 'files_antivirus']
180
				);
181
			}
182
			// retry on error
183
			$isRetrySuccessful = $this->retry() && $this->writeRaw($data);
184
			$this->isAborted = !$isRetrySuccessful;
185
		}
186
	}
187
188
	/**
189
	 * @return bool
190
	 */
191
	protected function retry(){
192
		$this->initScanner();
193
		if (!is_null($this->lastChunk)) {
194
			return $this->writeRaw($this->lastChunk);
195
		}
196
		return true;
197
	}
198
199
	/**
200
	 * @param $data
201
	 * @return bool
202
	 */
203
	protected function writeRaw($data){
204
		$dataLength = strlen($data);
205
		$bytesWritten = @fwrite($this->getWriteHandle(), $data);
206
		if ($bytesWritten === $dataLength){
207
			$this->byteCount += $bytesWritten;
208
			$this->lastChunk = $data;
209
			return true;
210
		}
211
		return false;
212
	}
213
214
	/**
215
	 * Get a resource to write data into
216
	 * @return resource
217
	 */
218
	protected function getWriteHandle(){
219
		return $this->writeHandle;
220
	}
221
222
	/**
223
	 * Prepare chunk (if required)
224
	 * @param string $data
225
	 * @return string
226
	 */
227
	protected function prepareChunk($data){
228
		return $data;
229
	}
230
}
231