addNewWindow()   C
last analyzed

Complexity

Conditions 12
Paths 15

Size

Total Lines 91
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 12
eloc 45
c 3
b 0
f 0
nc 15
nop 2
dl 0
loc 91
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
// Copyright (C) 2014 Universitätsbibliothek Mannheim
4
// See file LICENSE for license details.
5
6
// Authors: Alexander Wagner, Stefan Weil
7
8
// Test whether the script was called directly (used for unit test). Use some
9
// heuristics to detect whether we are not running in a web application.
10
if (isset($unittest)) {
11
    $unittest = array();
12
}
13
$unittest[__FILE__] = !isset($_SERVER['SERVER_NAME']);
14
15
require_once('globals.php');
16
17
if (!$unittest[__FILE__]) {
18
    trace("QUERY_STRING=" . $_SERVER['QUERY_STRING']);
19
}
20
21
// Connect to database and get configuration constants.
22
require_once('DBConnector.class.php');
23
$db = new palma\DBConnector();
24
25
function displayCommand($cmd)
26
{
27
    if (defined('CONFIG_SSH')) {
28
        $cmd = CONFIG_SSH . " 'DISPLAY=" . CONFIG_DISPLAY . " $cmd'";
29
    } else {
30
        $cmd = "DISPLAY=" . CONFIG_DISPLAY . " HOME=/var/www $cmd";
31
    }
32
33
    monitor("control.php: displayCommand $cmd");
34
35
    // add directories with palma-browser to PATH
36
    $result = shell_exec('PATH=/usr/lib/palma:./scripts:$PATH '.$cmd);
37
    trace("displayCommand $cmd, result=$result");
38
    return $result;
39
}
40
41
function wmClose($id)
42
{
43
    monitor("control.php: wmClose");
44
    // Close window gracefully.
45
    displayCommand("wmctrl -i -c $id");
46
}
47
48
function wmHide($id)
49
{
50
    monitor("control.php: wmHide");
51
    // Hide window. This is done by moving it to desktop 1.
52
    displayCommand("wmctrl -i -r $id -t 1");
53
}
54
55
function wmShow($id)
56
{
57
    monitor("control.php: wmShow");
58
    // Show window on current desktop.
59
    displayCommand("wmctrl -i -R $id");
60
}
61
62
function windowListOnScreen()
63
{
64
    monitor("control.php: windowListOnScreen");
65
    $list = array();
66
    $windows = explode("\n", displayCommand('wmctrl -l'));
67
    foreach ($windows as $w) {
68
        $field = explode(' ', $w);
69
        $id = $field[0];
70
        if ($id != '') {
71
            array_push($list, $id);
72
        }
73
    }
74
    return $list;
75
}
76
77
// simple list with content from database
78
79
function windowList()
80
{
81
    monitor("control.php: windowList");
82
    $list = array();
83
    global $db;
84
85
    // Get ordered list of all windows from the database.
86
    $windows = $db->getWindows();
87
    foreach ($windows as $w) {
88
        $id = $w['win_id'];
89
        if ($id != '') {
90
            array_push($list, $id);
91
        }
92
    }
93
    return $list;
94
}
95
96
function closeAll()
97
{
98
    monitor("control.php: closeAll");
99
    global $db;
100
101
    $windows_on_screen = windowListOnScreen();
102
103
    foreach ($windows_on_screen as $id) {
104
        wmClose($id);
105
        if ($db->getWindowState($id) != null) {
106
            $db->deleteWindow($id);
107
        }
108
    }
109
110
    // Remove any remaining window entries in database.
111
    $db->exec('DELETE FROM window');
112
113
    // Remove any remaining files in the upload directory.
114
    clearUploadDir();
115
}
116
117
function doLogout($username)
118
{
119
    monitor("control.php: doLogout");
120
    global $db;
121
    if ($username == 'ALL') {
122
        // Terminate all user connections and reset system.
123
        closeAll();
124
        $db->resetTables();
125
    }
126
}
127
128
function clearUploadDir()
129
{
130
    monitor("control.php: clearUploadDir");
131
    # Remove all files in the upload directory.
132
    if (is_dir(CONFIG_UPLOAD_DIR)) {
133
        if ($dh = opendir(CONFIG_UPLOAD_DIR)) {
134
            while (($file = readdir($dh)) !== false) {
135
                if ($file != "." and $file != "..") {
136
                    unlink(CONFIG_UPLOAD_DIR . "/$file");
137
                }
138
            }
139
            closedir($dh);
140
        }
141
    }
142
}
143
144
function setLayout($layout)
145
{
146
    monitor("control.php: setLayout $layout");
147
    // Set layout of team display. Layouts are specified by their name.
148
    // We use names like g1x1, g2x1, g1x2, ...
149
    // Restore the last layout if the function is called with a null argument.
150
151
    global $db;
152
153
    trace("layout $layout");
154
155
    $geom = array();
156
    $geom['g1x1'] = array(
157
                        array(0, 0, 1, 1)
158
                    );
159
    $geom['g2x1'] = array(
160
                        array(0, 0, 2, 1), array(1, 0, 2, 1)
161
                    );
162
    $geom['g1x2'] = array(
163
                        array(0, 0, 1, 2), array(0, 1, 1, 2)
164
                    );
165
    $geom['g1a2'] = array(
166
                        array(0, 0, 2, 1), array(1, 0, 2, 2),
167
                        array(1, 1, 2, 2)
168
                    );
169
    $geom['g2x2'] = array(
170
                        array(0, 0, 2, 2), array(1, 0, 2, 2),
171
                        array(0, 1, 2, 2), array(1, 1, 2, 2)
172
                    );
173
174
    if ($layout == null) {
175
        $layout = $db->querySingle("SELECT value FROM setting WHERE key='layout'");
176
    }
177
178
    // Make sure $layout is valid
179
    if (!array_key_exists($layout, $geom)) {
180
        trace("layout invalid!");
181
    } else {
182
        $db->exec("UPDATE setting SET value='$layout' WHERE key='layout'");
183
        $dim = $geom[$layout];
184
185
        // Make sure that desktop 0 is selected.
186
        displayCommand('wmctrl -s 0');
187
188
        // Get width and height of desktop.
189
        $desktops = displayCommand("wmctrl -d");
190
        // $desktop looks like this.
191
        // 0  * DG: 1600x900  VP: 0,0  WA: 0,27 1600x873  Arbeitsfläche 1
192
        $fields = preg_split("/[\n ]+/", $desktops);
193
        $geom = preg_split("/x/", $fields[3]);
194
        $screenWidth = $geom[0];
195
        $screenHeight = $geom[1];
196
197
        // Show all windows for the current layout which are not disabled.
198
199
        $maxSection = count($dim);
200
        // Get ordered list of all windows from the database.
201
        $windows = $db->getWindows();
202
        foreach ($windows as $w) {
203
            $id = $w['win_id'];
204
            $enabled = $w['state'] == 'active';
205
            $section = $w['section'];
206
            if ($section >= 1 && $section <= $maxSection && $enabled) {
207
                // Show window, set size and position.
208
                $wi = $section - 1;
209
                $dx = $screenWidth / $dim[$wi][2];
210
                $dy = $screenHeight / $dim[$wi][3];
211
                $x = $dim[$wi][0] * $dx;
212
                $y = $dim[$wi][1] * $dy;
213
                wmShow($id);
214
                displayCommand("wmctrl -i -r $id -e 0,$x,$y,$dx,$dy");
215
            } else {
216
                // Hide window.
217
                wmHide($id);
218
            }
219
        }
220
    }
221
}
222
223
function activateControls($windowhex)
224
{
225
    global $db;
226
    $fhandler = $db->querySingle("SELECT handler FROM window WHERE win_id='$windowhex'");
227
    trace("activateControls for handler $fhandler");
228
    monitor("control.php: activateControls $fhandler");
229
}
230
231
function addNewWindow($db, $new)
232
{
233
    // Add a new window to the monitor. This window either uses the first
234
    // unused section or it will be hidden.
235
236
    monitor('control.php: addNewWindow ' . serialize($new));
237
    trace('addNewWindow ' . serialize($new));
238
    // '$new' already contains 'file', 'handler' and 'date', as well as the
239
    // username for VNC connections only.
240
    // 'win_id', 'section' have to be defined afterwards.
241
242
    // Get new window. Wait up to 10 s for it.
243
    $t_total = 0;
244
    do {
245
        $window_ids_on_screen = windowListOnScreen();
246
        $windows_in_db = $db->getWindows();
247
248
        $existing_ids = array();
249
        $new_window_id = '';
250
251
        if (count($windows_in_db) > 0) {
252
            // Add db windows to existing_ids.
253
            foreach ($windows_in_db as $win) {
254
                $existing_ids[] = $win['win_id'];
255
            }
256
257
            $new_window = array_diff($window_ids_on_screen, $existing_ids);
258
            foreach ($new_window as $win_id) {
259
                if ($win_id != "") {
260
                    $new_window_id = $win_id;
261
                }
262
            }
263
        } elseif (!empty($window_ids_on_screen)) {
264
            $new_window_id = $window_ids_on_screen[0];
265
        }
266
    } while (!$new_window_id && $t_total++ <= 10 && !sleep(1));
267
268
    if (!$new_window_id) {
269
        trace('warning: no new window found');
270
        return;
271
    }
272
273
    trace("new window $new_window_id");
274
275
    // Determine last assigned monitor section.
276
    //~ $max_section = $db->querySingle('SELECT MAX(section) FROM window');
277
278
    // Get first unused monitor section.
279
    $section = $db->nextID();
280
281
    // If all information is available, create window object.
282
283
    $new['id'] = $section;
284
    $new['section'] = $section;
285
286
    if ($section <= 4) {
287
        $new['state'] = "active";
288
    } else {
289
        // All sections are used, so there is no free one for the new window.
290
        $new['state'] = "inactive";
291
        // We could hide the new window immediately, but don't do it here:
292
        // Each new window will be shown in the middle of the screen.
293
        //~ wmHide($new_window_id);
294
        //~ trace("hide new window $new_window_id");
295
    }
296
297
    // $new['file'] = $active_window; (?)
298
299
    // TODO: check how to insert the userid for all content, not just vnc.
300
    // Perhaps better add to array in upload.php ?
301
    $userid = "";
302
    $queryid = $db->querySingle('SELECT user.userid FROM user WHERE user.name="' . $new['userid'] .'"');
303
    if (!empty($queryid)) {
304
        $userid = $queryid;
305
    } else {
306
        $userid = "all";
307
    }
308
309
    $myWindow = array(
310
        $new['id'],
311
        $new_window_id,
312
        $new['section'],
313
        $new['state'],
314
        $new['file'],
315
        $new['handler'],
316
        $userid,
317
        $new['date']
318
    );
319
320
    // Save window in database.
321
    $db->insertWindow($myWindow);
322
}
323
324
function createNewWindowSafe($db, $w)
325
{
326
    $inFile = $w['file'];
327
    if (!file_exists($inFile)) {
328
        trace("".escapeshellarg($inFile)." is not a file, aborting display");
329
        return;
330
    }
331
  
332
    require_once('FileHandler.class.php');
333
    list ($handler, $targetFile) =
334
      palma\FileHandler::getFileHandler($inFile);
335
    trace("file is now $targetFile, its handler is $handler");
336
337
    $window = array(
338
      "id" => "",
339
      "win_id" => "",
340
      "name" => "",
341
      "state" => "",
342
      "file" => $targetFile,
343
      "handler" => $handler,
344
      "userid" => "",
345
      "date" => $w['date']);
346
  
347
    createNewWindow($db, $window);
348
}
349
350
function createNewWindow($db, $w)
351
{
352
    // '$w' already contains 'file', 'handler' and 'date'.
353
    // 'win_id', 'section' have to be defined afterwards.
354
355
    $handler = $w['handler'];
356
    // TODO: use escapeshellarg() for filename.
357
    $filename = $w['file'];
358
359
    $cmd = "$handler ".escapeshellarg($filename);
360
    displayCommand("/usr/bin/nohup $cmd >/dev/null 2>&1 &");
361
362
    addNewWindow($db, $w);
363
    monitor("control.php: createNewWindow");
364
}
365
366
function processRequests($db)
367
{
368
    monitor("control.php: processRequests");
369
    if (array_key_exists('window', $_REQUEST)) {
370
        // All windows related commands must start with window=.
371
372
        $windownumber = escapeshellcmd($_REQUEST['window']);
373
        $windowname = false;
374
        $windowhex = 0;
375
        // TODO: $win_id und $windowname können vermutlich zusammengefasst werden.
376
        $win_id = 0;
377
378
        if ($windownumber != 'vncwin') {
379
            // This is the normal case.
380
            // Special handling is needed when called with window=vncwin, see below.
381
            $window = $windownumber - 1;
382
383
            $win_id = $db->getWindowIDBySection($windownumber);
384
            $windowlist = windowList();
385
386
            if (count($windowlist) == 0) {
387
                trace("no window found for command");
388
            } else if (is_numeric($window) && count($windowlist) <= $window) {
389
                trace("window $window is out of bounds");
390
            } else if (!is_numeric($window)) {
391
                trace("unhandled window $window");
392
            } else {
393
                $windowname = $windowlist[$window];
394
                $windowhex = hexdec($windowname);
395
            }
396
        }
397
398
        if ($windowname && array_key_exists('key', $_REQUEST)) {
399
            $key = escapeshellcmd($_REQUEST['key']);
400
            trace("key '$key' in window '$windownumber'");
401
            wmShow($windowname);
402
            // activateControls($windowhex);
403
            // displayCommand("xdotool windowfocus $windowhex key $key");
404
405
            // trying mousemove and click for better vnc control
406
            displayCommand("xdotool mousemove --window $windowhex 100 100 " .
407
                       "key $key");
408
        }
409
410
        if ($windowname && array_key_exists('keydown', $_REQUEST)) {
411
            // TODO: keydown is currently mapped to key because we had problems
412
            // with sticking keys (no keyup seen). This should be fixed by a
413
            // better event handling.
414
            $key = escapeshellcmd($_REQUEST['keydown']);
415
            trace("keydown '$key' in window '$windownumber'");
416
            wmShow($windowname);
417
            // activateControls($windowhex);
418
            // displayCommand("xdotool windowfocus $windowhex key $key");
419
420
            // trying mousemove and click for better vnc control
421
            displayCommand("xdotool mousemove --window $windowhex 100 100 " .
422
                       "key $key");
423
            //~ displayCommand("xdotool windowfocus $windowhex keydown $key");
424
        }
425
426
        if ($windowname && array_key_exists('keyup', $_REQUEST)) {
427
            // TODO: keyup is currently ignored, see comment above.
428
            $key = escapeshellcmd($_REQUEST['keyup']);
429
            trace("keyup '$key' in window '$windownumber'");
430
            // activateControls($windowhex);
431
            //~ wmShow($windowname);
432
            //~ displayCommand("xdotool windowfocus $windowhex keyup $key");
433
        }
434
435
        if (array_key_exists('delete', $_REQUEST)) {
436
            $delete = addslashes($_REQUEST['delete']);
437
            trace("delete='$delete', close window $windownumber");
438
439
            // Restrict deletion to files known in the db.
440
            // TODO: check if given file and section match the values in the DB,
441
            // but currently, both those values can be ambiguos
442
            $file_in_db = $db->querySingle("SELECT id FROM window WHERE file='$delete'");
443
            $delete = str_replace(" ", "\ ", $delete);
444
            trace("file in db: $file_in_db");
445
            if ($file_in_db) {
446
                if (file_exists($delete)) {
447
                    trace("+++ DELETE FILE FROM WEBINTERFACE +++");
448
                    unlink($delete);
449
                } elseif ($delete == "VNC") {
450
                    trace("+++ DELETE VNC Client FROM DAEMON +++");
451
                    // call via daemon: ?window=vncwin&delete=VNC&vncid=123
452
                    trace("vnc delete in control");
453
                    $win_id = escapeshellcmd($_REQUEST['vncid']);   // = hexWindow in database, but not on screen
454
                    trace("VNC via Daemon ... id=$win_id");
455
                } elseif (strstr($delete, "http")) {
456
                    trace("+++ DELETE Browserwindow +++");
457
                } elseif (preg_match('/(^\w{3,}@\w{1,})/', $delete)) {
458
                    trace("+++ DELETE VNC Client FROM WEBINTERFACE +++");
459
                    // call via webinterface
460
                    $win_id = $db->querySingle("SELECT win_id FROM window WHERE file='$delete' AND handler='vnc'");
461
                    trace("DELETE VNC Window with ID=$win_id FROM Database ::
462
                    SELECT win_id FROM window WHERE file='$delete' AND handler='vnc'");
463
                } else {
464
                    trace("Unhandled delete for '$delete'");
465
                }
466
                wmClose($win_id);
467
                $db->deleteWindow($win_id);
468
            } else {
469
                trace("Given file not present in database!");
470
            }
471
        }
472
473
        if (array_key_exists('closeOrphans', $_REQUEST)) {
474
            // win_ids in db
475
            $windows_in_db = $db->getWindows();
476
            $db_ids = array();
477
478
            if (count($windows_in_db) > 0) {
479
                foreach ($windows_in_db as $win) {
480
                    array_push($db_ids, $win['win_id']);
481
                }
482
            }
483
484
            // win_ids on screen
485
            $screen_ids = windowListOnScreen();
486
487
            // orphaned windows
488
            $orphan_ids = array_diff($screen_ids, $db_ids);
489
490
            if (count($orphan_ids) > 0) {
491
                // close windows on screen not existing in database
492
                foreach ($orphan_ids as $id) {
493
                    wmClose($id);
494
                }
495
            }
496
        }
497
498
        if (array_key_exists('toggle', $_REQUEST)) {
499
            // Change window state from visible to invisible and vice versa.
500
            $state = $db->getWindowState($win_id);
501
            trace("toggle window $windownumber, id=$win_id, state=$state");
502
            if ($state == "active") {
503
                wmHide($win_id);
504
                $db->setWindowState($win_id, "inactive");
505
            } else {
506
                wmShow($win_id);
507
                $db->setWindowState($win_id, "active");
508
            }
509
        }
510
    } elseif (array_key_exists('layout', $_REQUEST)) {
511
        setLayout(escapeshellcmd($_REQUEST['layout']));
512
    } elseif (array_key_exists('logout', $_REQUEST)) {
513
        doLogout($_REQUEST['logout']);
514
    } elseif (array_key_exists('newVncWindow', $_REQUEST)) {
515
        // TODO: Better write new code for VNC window.
516
        addNewWindow($db, unserialize(urldecode($_REQUEST['newVncWindow'])));
517
    } elseif (array_key_exists('newWindow', $_REQUEST)) {
518
        createNewWindowSafe($db, unserialize(urldecode($_REQUEST['newWindow'])));
519
    }
520
521
    if (array_key_exists('switchWindows', $_REQUEST)) {
522
        $before = escapeshellcmd($_REQUEST['before']);
523
        $after = escapeshellcmd($_REQUEST['after']);
524
        trace("switching $before and $after");
525
526
        // exchange section
527
        $win_id1 = $db->getWindowIDBySection($before);
528
        $win_id2 = $db->getWindowIDBySection($after);
529
530
        $db->updateWindow($win_id1, 'section', $after);
531
        $db->updateWindow($win_id2, 'section', $before);
532
533
        trace("++updating database $win_id1 section=$after");
534
        trace("++updating database $win_id2 section=$before");
535
536
        // Update display (no layout change).
537
        setLayout(null);
538
    }
539
540
    if (array_key_exists('openURL', $_REQUEST)) {
541
        $openURL = escapeshellcmd($_REQUEST['openURL']);
542
        trace("openURL $openURL");
543
544
        // If URL leads to pdf file, download it and treat as upload
545
        $headers = get_headers($openURL, 1);
546
        if ($headers["Content-Type"] == "application/pdf") {
547
            trace("url seems to lead to a pdf file, so downloading it...");
548
            $temp_name = basename($openURL);
549
            $temp_dir = "/tmp";
550
            file_put_contents("$temp_dir/$temp_name", file_get_contents($openURL));
551
            $mimetype = mime_content_type("$temp_dir/$temp_name");
552
            trace("mimetype is $mimetype");
553
            if ($mimetype == "application/pdf") {
554
                $_FILES['file']['name'] = "$temp_name";
555
                $_FILES['file']['tmp_name'] = "$temp_dir/$temp_name";
556
                $_FILES['file']['error'] = "downloaded_from_url";
557
                trace("Handing over to upload.php");
558
                include 'upload.php';
559
            } else {
560
                trace("Deleting file!");
561
                unlink("$temp_dir/$temp_name");
562
            }
563
        } else {
564
            $dt = new DateTime();
565
            $date = $dt->format('Y-m-d H:i:s');
566
            $window = array(
567
                "id" => "",
568
                "win_id" => "",
569
                "section" => "",
570
                "state" => "",
571
                "file" => $openURL,
572
                "handler" => "palma-browser",
573
                "userid" => "",
574
                "date" => $date
575
            );
576
            createNewWindow($db, $window);
577
        }
578
    }
579
580
    // TODO: check if query redundant?
581
    if (array_key_exists('closeAll', $_REQUEST)) {
582
        $close = $_REQUEST['closeAll'];
583
        trace("close all windows $close");
584
        closeAll();
585
    }
586
} // processRequests
587
588
if ($db->checkPermission() || $unittest[__FILE__]) {
589
    processRequests($db);
590
}
591
592
if ($unittest[__FILE__]) {
593
    // Experimental: Get function call from startx.
594
    parse_str(implode('&', array_slice($argv, 1)), $_GET);
595
    if (isset($_GET) && count($_GET) > 0) {
596
        foreach ($_GET as $key => $value) {
597
            // Only defined actions allowed.
598
            if ($key == "doLogout") {
599
                doLogout($value);
600
            }
601
        }
602
    } else {
603
        // Run unit test.
604
        echo("<p>Running unit test</p>");
605
        trace("Running unit test for " . __FILE__);
606
        trace("Finished unit test");
607
    }
608
}
609