1<?php
2/**
3 * Virtual machine PNG screenshot generation
4 *
5 * @author Ian Moore (imoore76 at yahoo dot com)
6 * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
7 * @version $Id: screen.php 591 2015-04-11 22:40:47Z imoore76 $
8 * @package phpVirtualBox
9 *
10 */
11
12# Turn off PHP notices
13error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_WARNING);
14
15require_once(dirname(__FILE__).'/lib/config.php');
16require_once(dirname(__FILE__).'/lib/utils.php');
17require_once(dirname(__FILE__).'/lib/vboxconnector.php');
18
19
20// Allow caching of some screenshot data
21@Header('ETag: "' . $_REQUEST['vm'].'_'.$_REQUEST['randid'].'"');
22session_cache_limiter('private_no_expire');
23
24// Check for valid session
25global $_SESSION;
26session_init();
27if(!@$_SESSION['valid']) {
28	return;
29}
30
31// Clean request
32$_REQUEST = array_merge(@$_GET,@$_POST);
33
34$settings = new phpVBoxConfigClass();
35$vbox = new vboxconnector();
36$vbox->connect();
37
38// Set width. Else assume we want real time updates if VM is running below
39if($_REQUEST['width']) {
40	$force_width = $_REQUEST['width'];
41}
42
43try {
44
45	// Is VM Specified
46	if(!$_REQUEST['vm']) {
47		echo("Please specify a VM to take a screen shot of. E.g. http://webserver/phpvirtualbox/screen.php?vm=VMName");
48		exit;
49	}
50
51	$machine = $vbox->vbox->findMachine($_REQUEST['vm']);
52
53	// Is snapshot specified?
54	if($_REQUEST['snapshot']) {
55
56		$snapshot = $machine->findSnapshot($_REQUEST['snapshot']);
57		$machine->releaseRemote();
58		$machine = &$snapshot->machine;
59
60	} else {
61
62		// Get machine state
63		switch($machine->state->__toString()) {
64			case 'Running':
65			case 'Saved':
66			case 'Restoring':
67				break;
68			default:
69				$machine->releaseRemote();
70				throw new Exception('The specified virtual machine is not in a Running state.');
71		}
72
73	}
74
75	// Date last modified
76	$dlm = floor($machine->lastStateChange/1000);
77
78	// Set last modified header
79	header("Last-Modified: " . gmdate("D, d M Y H:i:s", $dlm) . " GMT");
80
81	$_REQUEST['vm'] = $machine->id;
82
83
84
85	// Take active screenshot if machine is running
86	if(!$_REQUEST['snapshot'] && $machine->state->__toString() == 'Running') {
87
88		// Let the browser cache images for 3 seconds
89		$ctime = 0;
90		if(strpos($_SERVER['HTTP_IF_NONE_MATCH'],'_')) {
91			$ctime = preg_replace("/.*_/",str_replace('"','',$_SERVER['HTTP_IF_NONE_MATCH']));
92		} else if(strpos($_ENV['HTTP_IF_NONE_MATCH'],'_')) {
93			$ctime = preg_replace("/.*_/",str_replace('"','',$_ENV['HTTP_IF_NONE_MATCH']));
94		} else if(strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
95			$ctime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
96		} else if(strpos($_ENV['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
97			$ctime = strtotime($_ENV['HTTP_IF_MODIFIED_SINCE']);
98		}
99
100    	if($ctime >= (time()-3)) {
101			if (strpos(strtolower(php_sapi_name()),'cgi') !== false) {
102				Header("Status: 304 Not Modified");
103			} else {
104				Header("HTTP/1.0 304 Not Modified");
105			}
106      		exit;
107    	}
108
109
110    	header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
111
112		$vbox->session = $vbox->websessionManager->getSessionObject($vbox->vbox->handle);
113		$machine->lockMachine($vbox->session->handle,'Shared');
114		$machine->releaseRemote();
115
116
117		$res = $vbox->session->console->display->getScreenResolution(0);
118
119		$screenWidth = array_shift($res);
120	    $screenHeight = array_shift($res);
121
122
123	    // Force screenshot width while maintaining aspect ratio
124	    if($force_width) {
125
126			$factor  = (float)$force_width / (float)$screenWidth;
127
128			$screenWidth = $force_width;
129			if($factor > 0) {
130				$screenHeight = $factor * $screenHeight;
131			} else {
132				$screenHeight = ($screenWidth * 9.0/16.0);
133			}
134
135		// If no width is set, we were reached from Open in New Window
136	    } else if(!$_REQUEST['width']) {
137
138			//Set no caching
139			header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
140			header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
141			header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
142
143	    }
144
145	    // If we were unable to get screen dimensions, set it to something
146	    if(!$screenWidth || !$screenHeight) {
147	    	$screenWidth = 640;
148	    	$screenHeight = 480;
149	    }
150		// array() for compatibility with readSavedScreenshotPNGToArray return value
151		try {
152			$imageraw = array($vbox->session->console->display->takeScreenShotToArray(0, $screenWidth, $screenHeight, 'PNG'));
153		} catch (Exception $e) {
154			// For some reason this is required or you get "Could not take a screenshot (VERR_TRY_AGAIN)" in some cases.
155			// I think it's a bug in the Linux guest additions, but cannot prove it.
156			$vbox->session->console->display->invalidateAndUpdate();
157			$imageraw = array($vbox->session->console->display->takeScreenShotToArray(0, $screenWidth, $screenHeight, 'PNG'));
158		}
159
160		$vbox->session->unlockMachine();
161		$vbox->session->releaseRemote();
162
163	} else {
164
165		// Let the browser cache saved state images
166		$ctime = 0;
167		if(strpos($_SERVER['HTTP_IF_NONE_MATCH'],'_')) {
168			$ctime = preg_replace("/.*_/",str_replace('"','',$_SERVER['HTTP_IF_NONE_MATCH']));
169		} else if(strpos($_ENV['HTTP_IF_NONE_MATCH'],'_')) {
170			$ctime = preg_replace("/.*_/",str_replace('"','',$_ENV['HTTP_IF_NONE_MATCH']));
171		} else if(strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
172			$ctime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
173		} else if(strpos($_ENV['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
174			$ctime = strtotime($_ENV['HTTP_IF_MODIFIED_SINCE']);
175		}
176
177    	if($dlm <= $ctime) {
178			if (strpos(strtolower(php_sapi_name()),'cgi') !== false) {
179				Header("Status: 304 Not Modified");
180			} else {
181				Header("HTTP/1.0 304 Not Modified");
182			}
183      		exit;
184    	}
185
186
187    	if($_REQUEST['full']) $imageraw = $machine->readSavedScreenshotPNGToArray(0);
188    	else $imageraw = $machine->readSavedThumbnailToArray(0, 'PNG');
189
190		$machine->releaseRemote();
191
192	}
193	$vbox->session = null;
194
195	header("Content-type: image/png",true);
196
197	foreach($imageraw as $i) {
198		if(is_array($i)) echo(base64_decode($i[0]));
199	}
200
201
202} catch (Exception $ex) {
203
204	// Ensure we close the VM Session if we hit a error, ensure we don't have a aborted VM
205	if($vbox && $vbox->session && $vbox->session->handle) {
206		try {
207			$vbox->session->unlockMachine();
208			unset($vbox->session);
209		} catch (Exception $e) {
210		}
211	}
212
213	if($_REQUEST['full'] && strpos($ex->faultstring,'VERR_NOT_SUPPORTED') > 0) {
214		@header("Content-type: text/html");
215		echo("Screen shots are not supported by your VirtualBox installation. To enable screen shots, please install a VirtualBox exteionsion pack that supports VRDE ");
216		echo("such as the Oracle VM VirtualBox Extension Pack found in the Downloads section of <a href='http://www.virtualbox.org'>http://www.virtualbox.org</a>.");
217	} else if($_REQUEST['full'] || $_REQUEST['debug']) {
218		header("Content-type: text/html", true);
219		echo("<pre>");
220		print_r($ex);
221		echo("</pre>");
222	}
223}
224
225