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 OCA\Files_Antivirus\Db\Rule; |
12
|
|
|
use OCA\Files_Antivirus\Db\RuleMapper; |
13
|
|
|
use OCP\ILogger; |
14
|
|
|
|
15
|
|
|
class Status { |
16
|
|
|
|
17
|
|
|
/* |
18
|
|
|
* The file was not checked (e.g. because the AV daemon wasn't running). |
19
|
|
|
*/ |
20
|
|
|
public const SCANRESULT_UNCHECKED = -1; |
21
|
|
|
|
22
|
|
|
/* |
23
|
|
|
* The file was checked and found to be clean. |
24
|
|
|
*/ |
25
|
|
|
public const SCANRESULT_CLEAN = 0; |
26
|
|
|
|
27
|
|
|
/* |
28
|
|
|
* The file was checked and found to be infected. |
29
|
|
|
*/ |
30
|
|
|
public const SCANRESULT_INFECTED = 1; |
31
|
|
|
|
32
|
|
|
/* |
33
|
|
|
* Should be SCANRESULT_UNCHECKED | SCANRESULT_INFECTED | SCANRESULT_CLEAN |
34
|
|
|
*/ |
35
|
|
|
protected $numericStatus = self::SCANRESULT_UNCHECKED; |
36
|
|
|
|
37
|
|
|
/* |
38
|
|
|
* Virus name or error message |
39
|
|
|
*/ |
40
|
|
|
protected $details = ''; |
41
|
|
|
|
42
|
|
|
/** @var RuleMapper */ |
43
|
|
|
protected $ruleMapper; |
44
|
|
|
|
45
|
|
|
/** @var ILogger */ |
46
|
|
|
protected $logger; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Status constructor. |
50
|
|
|
* |
51
|
|
|
* @param RuleMapper $ruleMapper |
52
|
|
|
* @param ILogger $logger |
53
|
|
|
*/ |
54
|
1 |
|
public function __construct(RuleMapper $ruleMapper, ILogger $logger) { |
55
|
1 |
|
$this->ruleMapper = $ruleMapper; |
56
|
1 |
|
$this->logger = $logger; |
57
|
1 |
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Get scan status as integer |
61
|
|
|
* @return int |
62
|
|
|
*/ |
63
|
1 |
|
public function getNumericStatus() { |
64
|
1 |
|
return $this->numericStatus; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Get scan status as string |
69
|
|
|
* @return string |
70
|
|
|
*/ |
71
|
1 |
|
public function getDetails() { |
72
|
1 |
|
return $this->details; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
public function setNumericStatus(int $numericStatus): void { |
76
|
|
|
$this->numericStatus = $numericStatus; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
public function setDetails(string $details): void { |
80
|
|
|
$this->details = $details; |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @param string $rawResponse |
85
|
|
|
* @param integer $result |
86
|
|
|
*/ |
87
|
1 |
|
public function parseResponse($rawResponse, $result = null) { |
88
|
1 |
|
$matches = []; |
89
|
|
|
|
90
|
1 |
|
if (is_null($result)) { // Daemon or socket mode |
91
|
|
|
try { |
92
|
1 |
|
$allRules = $this->getResponseRules(); |
93
|
|
|
} catch (\Exception $e) { |
94
|
|
|
$this->logger->error(__METHOD__.', exception: '.$e->getMessage(), ['app' => 'files_antivirus']); |
|
|
|
|
95
|
|
|
return; |
96
|
|
|
} |
97
|
|
|
|
98
|
1 |
|
$isMatched = false; |
99
|
1 |
|
foreach ($allRules as $rule) { |
100
|
1 |
|
if (preg_match($rule->getMatch(), $rawResponse, $matches)) { |
|
|
|
|
101
|
1 |
|
$isMatched = true; |
102
|
1 |
|
$this->numericStatus = (int)$rule->getStatus(); |
|
|
|
|
103
|
1 |
|
if ((int)$rule->getStatus() === self::SCANRESULT_CLEAN) { |
|
|
|
|
104
|
1 |
|
$this->details = ''; |
105
|
|
|
} else { |
106
|
1 |
|
$this->details = isset($matches[1]) ? $matches[1] : 'unknown'; |
107
|
|
|
} |
108
|
1 |
|
break; |
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
|
112
|
1 |
|
if (!$isMatched) { |
113
|
1 |
|
$this->numericStatus = self::SCANRESULT_UNCHECKED; |
114
|
1 |
|
$this->details = 'No matching rules. Please check antivirus rules.'; |
115
|
|
|
} |
116
|
|
|
} else { // Executable mode |
117
|
|
|
$scanStatus = $this->ruleMapper->findByResult($result); |
118
|
1 |
|
if (is_array($scanStatus) && count($scanStatus)) { |
119
|
1 |
|
$this->numericStatus = (int)$scanStatus[0]->getStatus(); |
120
|
1 |
|
$this->details = $scanStatus[0]->getDescription(); |
121
|
1 |
|
} |
122
|
|
|
|
123
|
|
|
switch ($this->numericStatus) { |
124
|
1 |
|
case self::SCANRESULT_INFECTED: |
125
|
1 |
|
$report = []; |
126
|
1 |
|
$rawResponse = explode("\n", $rawResponse); |
127
|
1 |
|
|
128
|
|
|
foreach ($rawResponse as $line) { |
129
|
1 |
|
if (preg_match('/.*: (.*) FOUND\s*$/', $line, $matches)) { |
130
|
1 |
|
$report[] = $matches[1]; |
131
|
1 |
|
} |
132
|
|
|
} |
133
|
|
|
$this->details = implode(', ', $report); |
134
|
1 |
|
|
135
|
|
|
break; |
136
|
1 |
|
case self::SCANRESULT_UNCHECKED: |
137
|
1 |
|
if (!$this->details) { |
138
|
1 |
|
$this->details = 'No matching rule for exit code ' . $this->numericStatus .'. Please check antivirus rules configuration.' ; |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
|
} |
143
|
1 |
|
|
144
|
|
|
/** |
145
|
|
|
* @return Rule[] |
146
|
|
|
*/ |
147
|
|
|
protected function getResponseRules() { |
148
|
1 |
|
$infectedRules = $this->ruleMapper->findAllMatchedByStatus(self::SCANRESULT_INFECTED); |
149
|
1 |
|
$uncheckedRules = $this->ruleMapper->findAllMatchedByStatus(self::SCANRESULT_UNCHECKED); |
150
|
1 |
|
$cleanRules = $this->ruleMapper->findAllMatchedByStatus(self::SCANRESULT_CLEAN); |
151
|
1 |
|
|
152
|
|
|
$infectedRules = $infectedRules ? $infectedRules : []; |
153
|
1 |
|
$uncheckedRules = $uncheckedRules ? $uncheckedRules : []; |
154
|
1 |
|
$cleanRules = $cleanRules ? $cleanRules : []; |
155
|
1 |
|
|
156
|
|
|
// order: clean, infected, try to guess error |
157
|
|
|
return array_merge($cleanRules, $infectedRules, $uncheckedRules); |
158
|
1 |
|
} |
159
|
|
|
|
160
|
|
|
public function dispatch(Item $item) { |
161
|
|
|
switch ($this->getNumericStatus()) { |
162
|
|
|
case self::SCANRESULT_UNCHECKED: |
163
|
|
|
$item->processUnchecked($this); |
164
|
|
|
break; |
165
|
|
|
case self::SCANRESULT_INFECTED: |
166
|
|
|
$item->processInfected($this); |
167
|
|
|
break; |
168
|
|
|
case self::SCANRESULT_CLEAN: |
169
|
|
|
$item->processClean(); |
170
|
|
|
break; |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.