SSVNCDaemon::parseHostname()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
eloc 11
nc 4
nop 1
dl 0
loc 17
rs 9.9
c 1
b 1
f 0
1
<?php
2
3
namespace palma;
4
5
// Copyright (C) 2014-2023 Universitätsbibliothek Mannheim
6
// See file LICENSE for license details.
7
8
class SSVNCDaemon
9
{
10
  // all during session connected VNC Clients
11
  /** @var array<array<string>> */
12
  private array $VNC_CLIENTS = array();
13
14
  // count of all connected clients
15
  // private $_VNC_CLIENT_COUNT;
16
17
  // active client count
18
  private int $CONNECTIONS = 0;
19
20
  // ignored VNC Clients
21
  // TODO: check if no more longer helpful, has to be cleaned
22
  ///** @var array<string> */
23
  //private array $IGNORE_LIST = array();
24
25
  public DBConnector $db;
26
27
  public function __construct()
28
  {
29
    require_once 'DBConnector.class.php';
30
    $this->db = DBConnector::getInstance();
31
32
    // Start Vncviewer
33
    $handle = $this->start();
34
35
    // Read log in loop
36
    $this->readLog($handle);
37
  }
38
39
  protected function start(): mixed
40
  {
41
    // Startup ssvncviewer in multilisten mode
42
    require_once 'globals.php';
43
    $cmd = "export DISPLAY=" . CONFIG_DISPLAY .
44
           "; killall -q ssvncviewer; ssvncviewer -viewonly -multilisten 0 2>&1";
45
    $handle = popen($cmd, 'r');
46
    trace("SSVNCDaemon::start: vnc_viewer started");
47
48
    return $handle;
49
  }
50
51
  protected function readLog(mixed $handle): void
52
  {
53
    require_once 'globals.php';
54
    trace("SSVNCDaemon::readLog()");
55
56
    // local SSVNC client info
57
    $client = array(
58
      "ip" => "",
59
      "hostname" => "",
60
      "active" => 1,
61
      "exit" => 0
62
    );
63
64
    // Read File continuously
65
    while (!feof($handle)) {
66
      $buffer = fgets($handle);
67
68
      if ($this->CONNECTIONS == 0) {
69
        //debug("SSVNCDaemon::readLog: WAITING FOR NEW CONNECTIONS");
70
      }
71
72
      $ip = $this->parseIP($buffer);
73
      $hostname = $this->parseHostname($buffer);
74
      $exit = $this->parseExit($buffer);
75
76
      if ($ip != "") {
77
        $client["ip"] = $ip;
78
      }
79
      if ($hostname != "") {
80
        $client["hostname"] = $hostname;
81
      }
82
      if ($exit != 0) {
83
        $client["exit"] = $exit;
84
      }
85
86
      if (
87
          strstr($buffer, 'create_image') && $client["ip"] != "" &&
88
          $client["hostname"] != ""
89
      ) {
90
        // add client
91
        if ($client["hostname"] == "unknown") {
92
          $client["hostname"] = $client["ip"];
93
        }
94
        $this->addClient($client["ip"], $client["hostname"]);
95
96
        // reset local Client information after adding it
97
        $client["ip"] = "";
98
        $client["hostname"] = "";
99
      }
100
101
      if ($exit == 1) {
102
        // decrease active client count
103
        if ($this->CONNECTIONS > 0) {
104
          $this->CONNECTIONS--;
105
          $this->deleteInactiveVncWindow();
106
        }
107
      }
108
109
      $halt = $this->CONNECTIONS;
110
111
      if ($halt == -1) {
112
        exit(0);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
113
      }
114
115
      flush();
116
    }
117
118
    pclose($handle);
119
  }
120
121
  protected function parseIP(string $buffer): string
122
  {
123
    require_once 'globals.php';
124
    $ip = "";
125
    $line = $buffer;
126
    if (strpos($line, "Reverse VNC connection from IP")) {
127
      $line = trim($line);
128
      $item = explode(":", $line);
129
      $ip = trim($item[1]);
130
      $ip = preg_replace('/\W\W\d{4}\/\d{2}\/\d{2} \d{2}/', '', $ip);
131
    }
132
133
    if ($ip != "") {
134
      debug("SSVNCDaemon::parseIP(): " . $ip);
135
    }
136
137
    return $ip;
138
  }
139
140
  protected function parseHostname(string $buffer): string
141
  {
142
    require_once 'globals.php';
143
    $hostname = "";
144
    $line = $buffer;
145
    if (strpos($line, "Hostname")) {
146
      $line = trim($line);
147
      $item = explode(":", $line);
148
      $hostname = trim($item[1]);
149
      $hostname = preg_replace('/\.uni\-mannheim\.de/', '', $hostname);
150
    }
151
152
    if ($hostname != "") {
153
      debug("SSVNCDaemon::parseHostname(): " . $hostname);
154
    }
155
156
    return $hostname;
157
  }
158
159
  protected function parseExit(string $line): int
160
  {
161
    require_once 'globals.php';
162
    $exit = 0;
163
    if (strpos($line, "VNC Viewer exiting")) {
164
      $exit = 1;
165
    }
166
167
    if ($exit != 0) {
168
      debug("SSVNCDaemon::parseExit: exit code " . $exit);
169
    }
170
171
    return $exit;
172
  }
173
174
  protected function addClient(string $ip, string $hostname): void
175
  {
176
    require_once 'globals.php';
177
    $vncclient = array(
178
      "ip" => $ip,
179
      "hostname" => $hostname,
180
      "active" => 1,
181
      "exit" => 0
182
    );
183
    if (count($this->VNC_CLIENTS) == 0) {
184
      $id = 1;
185
    } else {
186
      $id = count($this->VNC_CLIENTS) + 1;
187
    }
188
189
    $this->VNC_CLIENTS[$id] = $vncclient;
190
191
    debug("SSVNCDaemon::addClient: CLIENT OBJECT with ID " . $id . " CREATED : "
192
        . $vncclient["ip"] . " | " . $vncclient["hostname"] . " | "
193
        . $vncclient["active"] . " | " . $vncclient["exit"]);
194
    debug("SSVNCDaemon::addClient: " . ($this->CONNECTIONS + 1) . " CLIENT(S) CONNECTED ...");
195
196
    $this->sendVncWindowToNuc($id, $vncclient);
197
198
    $this->CONNECTIONS++;
199
  }
200
201
  /**
202
   * @param array<string,string> $vncclient
203
   */
204
  protected function sendVncWindowToNuc(int $id, array $vncclient): void
205
  {
206
    require_once 'globals.php';
207
    debug("SSVNCDaemon::sendVncWindowToNuc()");
208
209
    $vnc_id = $vncclient["hostname"] . "-" . $id;
210
211
    // already existing in db?
212
    $clients_in_db = array();
213
    $client_info = $this->db->getVNCClientInfo();
214
215
    foreach ($client_info as $info) {
216
      $clients_in_db[] = $info["file"];
217
    }
218
219
    $ip = $vncclient["ip"];
220
    $name = $this->db->querySingle('SELECT user.name FROM address, user
221
                                     WHERE user.userid = (
222
                                     SELECT address.userid
223
                                     FROM address
224
                                     WHERE address.address ="' . $ip . '"
225
                                     )');
226
227
    // print("\n[Daemon]: USERNAME : " . $name);
228
229
    // $vncClientCount = $this->db->querySingle('SELECT count(id) FROM window WHERE handler = "vnc"');
230
    // $vncClientsAll = $this->db->query("SELECT user.name FROM address");
231
232
    // print("\n[Daemon]: clients in db = " . $vncClientCount);
233
    // print("\n[Daemon]: clients ignored = " . serialize($this->IGNORE_LIST));
234
235
    // if vnc_id not in database create window and send to nuc
236
    if (!(in_array($vnc_id, $clients_in_db))) {
237
      // print("\n[Daemon]: insert $vnc_id into db");
238
239
      $dt = new \DateTime();
240
      $date = $dt->format('Y-m-d H:i:s');
241
242
      $window = array(
243
        "id" => "",
244
        "win_id" => "",
245
        "name" => "",
246
        "state" => "",
247
        "file" => $name . "@" . $vnc_id,
248
        "handler" => "vnc",
249
        "userid" => $name,
250
        "date" => $date
251
      );
252
253
      $serializedWindow = serialize($window);
254
255
      require_once 'globals.php';
256
257
      $sw = urlencode($serializedWindow);
258
      // Get cURL resource
259
      $curl = curl_init();
260
      // Set some options - we are passing in a useragent too here
261
      curl_setopt_array($curl, array(
262
        CURLOPT_RETURNTRANSFER => 1,
263
        CURLOPT_URL => CONFIG_CONTROL_FILE . '?newVncWindow=' . $sw,
264
        CURLOPT_USERAGENT => 'PalMA cURL Request'
265
      ));
266
      // Send the request
267
      curl_exec($curl);
268
      // Close request to clear up some resources
269
      curl_close($curl);
270
    }
271
272
    // add unique id to ignore list after sending to nuc
273
    //array_push($this->IGNORE_LIST, $vnc_id);
274
  }
275
276
  protected function deleteInactiveVncWindow(): void
277
  {
278
    require_once 'globals.php';
279
    debug('SSVNCDaemon::deleteInactiveVncWindow()');
280
281
    // window_ids in db
282
    $vnc_windows_in_db = array();
283
    $client_info = $this->db->getVNCClientInfo();
284
    foreach ($client_info as $info) {
285
      $vnc_windows_in_db[] = $info["win_id"];
286
    }
287
288
    // window_ids on screen
289
    $windows_on_screen = array();
290
    $windows = explode("\n", shell_exec('wmctrl -l'));
291
292
    foreach ($windows as $w) {
293
      $field = explode(' ', $w);
294
      $id = $field[0];
295
      if ($id != '') {
296
        $windows_on_screen[] = $id;
297
      }
298
    }
299
300
    debug("  clients in db = " . serialize($vnc_windows_in_db));
301
    debug("  client on screen = " . serialize($windows_on_screen));
302
303
    // window_ids that are in the db, but not on the screen (window already closed)
304
    $inactive_vnc_window_ids = array_diff($vnc_windows_in_db, $windows_on_screen);
305
306
    foreach ($inactive_vnc_window_ids as $inactive_win_id) {
307
      // define vnc-id
308
      $inactive_vnc_id = $this->db->querySingle("SELECT file FROM window WHERE win_id='" . $inactive_win_id . "'");
0 ignored issues
show
Unused Code introduced by
The assignment to $inactive_vnc_id is dead and can be removed.
Loading history...
309
310
      // delete from database (send to control.php)
311
      $curl = curl_init();
312
313
      require_once 'globals.php';
314
      curl_setopt_array($curl, array(
315
        CURLOPT_RETURNTRANSFER => 1,
316
        CURLOPT_URL => CONFIG_CONTROL_FILE .
317
                     '?window=vncwin&delete=VNC&vncid=' . $inactive_win_id,
318
        CURLOPT_USERAGENT => 'PalMA cURL Request'
319
      ));
320
321
      curl_exec($curl);
322
      curl_close($curl);
323
324
      // debug(" inactive vnc_id = $inactive_vnc_id >> add to list: " .serialize($this->IGNORE_LIST));
325
    }
326
  }
327
}
328