summaryrefslogtreecommitdiffstats
path: root/modules-available/remoteaccess/api.inc.php
blob: bebe076788c480aad769bac39f4248b9b54f538b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php
// Api call for reserving a client
if (Request::any('action') === 'reserve') {
	$lectureid = Request::any('lectureid', '', 'string');
	$editid = Request::any('editid', '', 'string');
	$now = time();

	// For now only considere pcs, that are already in the remote db and pc in idle state
	//$client = Database::queryFirst("SELECT m.machineuuid, m.clientip, r.password, r.vncport FROM machine m INNER JOIN remoteaccess_machine r ON r.machineuuid = m.machineuuid WHERE m.currentsession IS NULL AND m.state = 'IDLE'");
	// Sometimes currentsession is not null while pc is in idle state ... bug?
	$client = Database::queryFirst("SELECT m.machineuuid, m.clientip, r.password, r.vncport FROM machine m INNER JOIN remoteaccess_machine r ON r.machineuuid = m.machineuuid WHERE m.state = 'IDLE'");

	// If there is a client reserve it
	if ($client && $editid) {
		Database::exec("UPDATE remoteaccess_machine SET timestamp=:now, lectureid=:lectureid, editid=:editid WHERE machineuuid=:machineuuid", ['now' => $now, 'lectureid' => $lectureid, 'editid' => $editid, 'machineuuid' => $client['machineuuid']]);
		echo json_encode($client);
	} else {
		echo "Currently all machines are occupied, try again later!";
	}

	// Exit the call, so the rest doesn't get executed
	exit(0);
} elseif (Request::any('action') === 'scp') {
	// Start scp taskmanager task to download the qemu snapshot
	$editid = Request::any('editid', '', 'string');
	$client = Database::queryFirst("SELECT m.machineuuid, m.clientip, r.editid, r.lectureid FROM machine m INNER JOIN remoteaccess_machine r ON r.machineuuid = m.machineuuid WHERE editid=:editid", ['editid' => $editid]);

	if (!$client) {
            die("Invalid edit id");
	}

	// Check if client edit was allowed anyways.
	// TODO does slx-admin even has the information if a user can edit a specific vm?

	// Start taskmanager task
	$task = Taskmanager::submit('ScpSnapshot', array(
		'clientIp' => $client['clientip'],
		'editId' => $client['editid'],
		'lectureId' => $client['lectureid'],
	));
	if (Taskmanager::isTask($task)) {
            die(json_encode($task));
	} else {
	    die('Taskmanager could not start the task');
	}
	exit(0);
}

$ip = $_SERVER['REMOTE_ADDR'];
if (substr($ip, 0, 7) === '::ffff:') $ip = substr($ip, 7);

$password = Request::post('password', false, 'string');
if ($password !== false) {
	$c = Database::queryFirst("SELECT machineuuid FROM machine WHERE clientip = :ip", ['ip' => $ip]);
	if ($c !== false) {
		$vncport = Request::post('vncport', 5900, 'int');
		Database::exec("INSERT INTO remoteaccess_machine (machineuuid, password, vncport)
				VALUES (:uuid, :passwd, :vncport)
				ON DUPLICATE KEY UPDATE
					password = VALUES(password), vncport = VALUES(vncport)",
			['uuid' => $c['machineuuid'], 'passwd' => $password, 'vncport' => $vncport]);
	}
	exit;
}

$range = IpUtil::parseCidr(Property::get(RemoteAccess::PROP_ALLOWED_VNC_NET));
if ($range === false) {
	die('No allowed IP defined');
}
$iplong = ip2long($ip);
if (PHP_INT_SIZE === 4) {
	$iplong = sprintf('%u', $iplong);
}
if ($iplong < $range['start'] || $iplong > $range['end']) {
	die('Access denied');
}

$headers = getallheaders();
$version = false;
if (!empty($headers['Bwlp-Plugin-Build-Revision'])) {
	$version = substr($headers['Bwlp-Plugin-Build-Revision'], 0, 6);
	if (!empty($headers['Bwlp-Plugin-Build-Timestamp'])) {
		$ts = $headers['Bwlp-Plugin-Build-Timestamp'];
		if (is_numeric($ts)) {
			if ($ts > 9999999999) {
				$ts = round($ts / 1000);
			}
			$ts = date('d.m.Y H:i', $ts);
		}
		$version .= ' (' . $ts . ')';
	}
}
Property::set(RemoteAccess::PROP_PLUGIN_VERSION,  $version, 2880);

Header('Content-Type: application/json');

$remoteLocations = RemoteAccess::getEnabledLocations();

if (empty($remoteLocations)) {
	$rows = [];
} else {
// TODO fail-counter for WOL, so we can ignore machines that apparently can't be woken up
// -> Reset counter in our ~poweron hook, but only if the time roughly matches a WOL attempt (within ~5 minutes)
	$rows = Database::queryAll("SELECT m.clientip, m.locationid, m.state, ram.password, ram.vncport, ram.woltime FROM machine m
	LEFT JOIN remoteaccess_machine ram ON (ram.machineuuid = m.machineuuid AND (ram.password IS NOT NULL OR m.state <> 'IDLE'))
	LEFT JOIN runmode r ON (r.machineuuid = m.machineuuid)
	WHERE m.locationid IN (:locs)
		AND r.machineuuid IS NULL",
		['locs' => $remoteLocations]);

	$wolCut = time() - 90;
	foreach ($rows as &$row) {
		if (($row['state'] === 'OFFLINE' || $row['state'] === 'STANDBY') && $row['woltime'] > $wolCut) {
			$row['wol_in_progress'] = true;
		}
		settype($row['locationid'], 'int');
		settype($row['vncport'], 'int');
		unset($row['woltime']);
	}
}

$groups = Database::queryAll("SELECT g.groupid AS id, g.groupname AS name,
       GROUP_CONCAT(l.locationid) AS locationids, g.passwd AS password
	FROM remoteaccess_group g INNER JOIN remoteaccess_x_location l USING (groupid)
	WHERE g.active = 1
	GROUP BY g.groupid");
foreach ($groups as &$group) {
	$group['locationids'] = explode(',', $group['locationids']);
	if (empty($group['password'])) {
		unset($group['password']);
	}
	settype($group['id'], 'int');
	foreach ($group['locationids'] as &$lid) {
		settype($lid, 'int');
	}
}

$fakeid = 100000;
echo json_encode(['clients' => $rows, 'locations' => $groups]);

// WTF, this makes the server return a 500 -.-
//fastcgi_finish_request();

RemoteAccess::ensureMachinesRunning();