ILIAS  release_7 Revision v7.30-3-g800a261c036
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilVirusScannerICapRemote.php
Go to the documentation of this file.
1 <?php
2 //hsuhh-patch: begin
3 /* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
4 
13 require_once "./Services/VirusScanner/classes/class.ilVirusScanner.php";
14 
16 {
18  private $host;
20  private $port;
22  private $socket;
24  public $userAgent = 'PHP-CLIENT/0.1.0';
25 
31  public function __construct($a_scancommand, $a_cleancommand)
32  {
33  parent::__construct($a_scancommand, $a_cleancommand);
34  $this->host = IL_ICAP_HOST;
35  $this->port = IL_ICAP_PORT;
36  }
37 
42  public function options($service)
43  {
44  $request = $this->getRequest('OPTIONS', $service);
45  $response = $this->send($request);
46  if(strlen($response) > 0) {
47  return $this->parseResponse($response);
48  }
49  return [];
50  }
51 
59  public function getRequest($method, $service, $body = [], $headers = [])
60  {
61  if (!array_key_exists('Host', $headers)) {
62  $headers['Host'] = $this->host;
63  }
64  if (!array_key_exists('User-Agent', $headers)) {
65  $headers['User-Agent'] = $this->userAgent;
66  }
67  if (!array_key_exists('Connection', $headers)) {
68  $headers['Connection'] = 'close';
69  }
70  $bodyData = '';
71  $hasBody = false;
72  $encapsulated = [];
73  foreach ($body as $type => $data) {
74  switch ($type) {
75  case 'req-hdr':
76  case 'res-hdr':
77  $encapsulated[$type] = strlen($bodyData);
78  $bodyData .= $data;
79  break;
80  case 'req-body':
81  case 'res-body':
82  $encapsulated[$type] = strlen($bodyData);
83  $bodyData .= dechex(strlen($data)) . "\r\n";
84  $bodyData .= $data;
85  $bodyData .= "\r\n";
86  $hasBody = true;
87  break;
88  }
89  }
90  if ($hasBody) {
91  $bodyData .= "0\r\n\r\n";
92  } elseif (count($encapsulated) > 0) {
93  $encapsulated['null-body'] = strlen($bodyData);
94  }
95  if (count($encapsulated) > 0) {
96  $headers['Encapsulated'] = '';
97  foreach ($encapsulated as $section => $offset) {
98  $headers['Encapsulated'] .= $headers['Encapsulated'] === '' ? '' : ', ';
99  $headers['Encapsulated'] .= "{$section}={$offset}";
100  }
101  }
102  $request = "{$method} icap://{$this->host}/{$service} ICAP/1.0\r\n";
103  foreach ($headers as $header => $value) {
104  $request .= "{$header}: {$value}\r\n";
105  }
106  $request .= "\r\n";
107  $request .= $bodyData;
108  return $request;
109  }
110 
115  public function send($request)
116  {
117  $response = '';
118  try{
119  $this->connect();
120  socket_write($this->socket, $request);
121  while ($buffer = socket_read($this->socket, 2048)) {
122  $response .= $buffer;
123  }
124  $this->disconnect();
125  } catch(ErrorException $e){
126  $this->log->warning("Cannot connect to icap://{$this->host}:{$this->port} (Socket error: " . $this->getLastSocketError() . ")");
127  }
128  return $response;
129  }
130 
135  private function connect()
136  {
137  $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
138  try{
139  if (!socket_connect($this->socket, $this->host, $this->port)) {
140  return false;
141  }
142  } catch(ErrorException $e){
143  throw $e;
144  }
145 
146  return true;
147  }
148 
153  public function getLastSocketError()
154  {
155  return socket_last_error($this->socket);
156  }
157 
161  private function disconnect()
162  {
163  socket_shutdown($this->socket);
164  socket_close($this->socket);
165  }
166 
171  private function parseResponse($string)
172  {
173  $response = [
174  'protocol' => [],
175  'headers' => [],
176  'body' => [],
177  'rawBody' => ''
178  ];
179  foreach (preg_split('/\r?\n/', $string) as $line) {
180  if ([] === $response['protocol']) {
181  if (0 !== strpos($line, 'ICAP/')) {
182  throw new RuntimeException('Unknown ICAP response');
183  }
184  $parts = preg_split('/ +/', $line, 3);
185  $response['protocol'] = [
186  'icap' => isset($parts[0]) ? $parts[0] : '',
187  'code' => isset($parts[1]) ? $parts[1] : '',
188  'message' => isset($parts[2]) ? $parts[2] : '',
189  ];
190  continue;
191  }
192  if ('' === $line) {
193  break;
194  }
195  $parts = preg_split('/: /', $line, 2);
196  if (isset($parts[0])) {
197  $response['headers'][$parts[0]] = isset($parts[1]) ? $parts[1] : '';
198  }
199  }
200  $body = preg_split('/\r?\n\r?\n/', $string, 2);
201  if (isset($body[1])) {
202  $response['rawBody'] = $body[1];
203  if (array_key_exists('Encapsulated', $response['headers'])) {
204  $encapsulated = [];
205  $params = preg_split('/, /', $response['headers']['Encapsulated']);
206  if (count($params) > 0) {
207  foreach ($params as $param) {
208  $parts = preg_split('/=/', $param);
209  if (count($parts) !== 2) {
210  continue;
211  }
212  $encapsulated[$parts[0]] = $parts[1];
213  }
214  }
215  foreach ($encapsulated as $section => $offset) {
216  $data = substr($body[1], $offset);
217  switch ($section) {
218  case 'req-hdr':
219  case 'res-hdr':
220  $response['body'][$section] = preg_split('/\r?\n\r?\n/', $data, 2)[0];
221  break;
222  case 'req-body':
223  case 'res-body':
224  $parts = preg_split('/\r?\n/', $data, 2);
225  if (count($parts) === 2) {
226  $response['body'][$section] = substr($parts[1], 0, hexdec($parts[0]));
227  }
228  break;
229  }
230  }
231  }
232  }
233  return $response;
234  }
235 
242  public function respMod($service, $body = [], $headers = [])
243  {
244  $request = $this->getRequest('RESPMOD', $service, $body, $headers);
245  $response = $this->send($request);
246  return $this->parseResponse($response);
247  }
248 
255  public function reqMod($service, $body = [], $headers = [])
256  {
257  $request = $this->getRequest('REQMOD', $service, $body, $headers);
258  $response = $this->send($request);
259  return $this->parseResponse($response);
260  }
261 }
262 //hsuhh-patch: end
$data
Definition: storeScorm.php:23
__construct($a_scancommand, $a_cleancommand)
ilVirusScannerICap constructor.
$section
Definition: Utf8Test.php:83
Interface to the ClamAV virus protector.
$param
Definition: xapitoken.php:29
$service
Definition: result.php:17
respMod($service, $body=[], $headers=[])
Base class for the interface to an external virus scanner This class is abstract and needs to be exte...
__construct(Container $dic, ilPlugin $plugin)
getLastSocketError()
Get last error code from socket object.
$response
reqMod($service, $body=[], $headers=[])
getRequest($method, $service, $body=[], $headers=[])