<?php
/**
 * Virtual machine PNG screenshot generation
 * 
 * @author Ian Moore (imoore76 at yahoo dot com)
 * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
 * @version $Id: screen.php 591 2015-04-11 22:40:47Z imoore76 $
 * @package phpVirtualBox
 * 
 */

# Turn off PHP notices
@error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_WARNING);

require_once(dirname(__FILE__).'/lib/config.php');
require_once(dirname(__FILE__).'/lib/utils.php');
require_once(dirname(__FILE__).'/lib/vboxconnector.php');


// Allow caching of some screenshot data
@Header('ETag: "' . $_REQUEST['vm'].'_'.$_REQUEST['randid'].'"');
session_cache_limiter('private_no_expire');

// Check for valid session
global $_SESSION;
session_init();
if(!@$_SESSION['valid']) {
	return;	
}

// Clean request
$_REQUEST = array_merge(@$_GET,@$_POST);

$settings = new phpVBoxConfigClass();
$vbox = new vboxconnector();
$vbox->connect();

// Set width. Else assume we want real time updates if VM is running below
if($_REQUEST['width']) {
	$force_width = $_REQUEST['width'];
}

try {

	// Is VM Specified
	if(!$_REQUEST['vm']) {
		echo("Please specify a VM to take a screen shot of. E.g. http://webserver/phpvirtualbox/screen.php?vm=VMName");
		exit;
	}

	$machine = $vbox->vbox->findMachine($_REQUEST['vm']);
	
	// Is snapshot specified?
	if($_REQUEST['snapshot']) {
		
		$snapshot = $machine->findSnapshot($_REQUEST['snapshot']);
		$machine->releaseRemote();
		$machine = &$snapshot->machine;
		
	} else {

		// Get machine state
		switch($machine->state->__toString()) {
			case 'Running':
			case 'Saved':
			case 'Restoring':
				break;
			default:
				$machine->releaseRemote();
				throw new Exception('The specified virtual machine is not in a Running state.');
		}

	}
	
	// Date last modified
	$dlm = floor($machine->lastStateChange/1000);

	// Set last modified header
	header("Last-Modified: " . gmdate("D, d M Y H:i:s", $dlm) . " GMT");

	$_REQUEST['vm'] = $machine->id;



	// Take active screenshot if machine is running
	if(!$_REQUEST['snapshot'] && $machine->state->__toString() == 'Running') {

		// Let the browser cache images for 3 seconds
		$ctime = 0;
		if(strpos($_SERVER['HTTP_IF_NONE_MATCH'] ?? '','_')) {
			$ctime = preg_replace("/.*_/",str_replace('"','',$_SERVER['HTTP_IF_NONE_MATCH']));
		} else if(strpos($_ENV['HTTP_IF_NONE_MATCH'] ?? '','_')) {
			$ctime = preg_replace("/.*_/",str_replace('"','',$_ENV['HTTP_IF_NONE_MATCH']));
		} else if(strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'] ?? '','GMT')) {
			$ctime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
		} else if(strpos($_ENV['HTTP_IF_MODIFIED_SINCE'] ?? '','GMT')) {
			$ctime = strtotime($_ENV['HTTP_IF_MODIFIED_SINCE']);
		}
		
    	if($ctime >= (time()-3)) {
			if (strpos(strtolower(php_sapi_name()),'cgi') !== false) {
				Header("Status: 304 Not Modified");
			} else {
				Header("HTTP/1.0 304 Not Modified");
			}
      		exit;
    	}
		

    	header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    	
		$vbox->session = $vbox->websessionManager->getSessionObject($vbox->vbox->handle);
		$machine->lockMachine($vbox->session->handle,'Shared');
		$machine->releaseRemote();
		
		
		$res = $vbox->session->console->display->getScreenResolution(0);
		
		$screenWidth = array_shift($res);
	    $screenHeight = array_shift($res);
	    

	    // Force screenshot width while maintaining aspect ratio
	    if($force_width) {

			$factor  = (float)$force_width / (float)$screenWidth;

			$screenWidth = $force_width;
			if($factor > 0) {
				$screenHeight = $factor * $screenHeight;
			} else {
				$screenHeight = ($screenWidth * 9.0/16.0);
			}

		// If no width is set, we were reached from Open in New Window
	    } else if(!$_REQUEST['width']) {

			//Set no caching
			header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
			header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
			header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
			
	    }

	    // If we were unable to get screen dimensions, set it to something
	    if(!$screenWidth || !$screenHeight) {
	    	$screenWidth = 640;
	    	$screenHeight = 480;
	    }
		// array() for compatibility with readSavedScreenshotPNGToArray return value
		try {
			$imageraw = array($vbox->session->console->display->takeScreenShotToArray(0, $screenWidth, $screenHeight, 'PNG'));
		} catch (Exception $e) {
			// For some reason this is required or you get "Could not take a screenshot (VERR_TRY_AGAIN)" in some cases.
			// I think it's a bug in the Linux guest additions, but cannot prove it.
			$vbox->session->console->display->invalidateAndUpdate();
			$imageraw = array($vbox->session->console->display->takeScreenShotToArray(0, $screenWidth, $screenHeight, 'PNG'));
		}

		$vbox->session->unlockMachine();
		$vbox->session->releaseRemote();
		
	} else {

		// Let the browser cache saved state images
		$ctime = 0;
		if(strpos($_SERVER['HTTP_IF_NONE_MATCH'],'_')) {
			$ctime = preg_replace("/.*_/",str_replace('"','',$_SERVER['HTTP_IF_NONE_MATCH']));
		} else if(strpos($_ENV['HTTP_IF_NONE_MATCH'],'_')) {
			$ctime = preg_replace("/.*_/",str_replace('"','',$_ENV['HTTP_IF_NONE_MATCH']));
		} else if(strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
			$ctime = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
		} else if(strpos($_ENV['HTTP_IF_MODIFIED_SINCE'],'GMT')) {
			$ctime = strtotime($_ENV['HTTP_IF_MODIFIED_SINCE']);
		}
		
    	if($dlm <= $ctime) {
			if (strpos(strtolower(php_sapi_name()),'cgi') !== false) {
				Header("Status: 304 Not Modified");
			} else {
				Header("HTTP/1.0 304 Not Modified");
			}
      		exit;
    	}
		
	
    	if($_REQUEST['full']) $imageraw = $machine->readSavedScreenshotPNGToArray(0);
    	else $imageraw = $machine->readSavedThumbnailToArray(0, 'PNG');
			
		$machine->releaseRemote();

	}
	$vbox->session = null;

	header("Content-type: image/png",true);

	foreach($imageraw as $i) {
		if(is_array($i)) echo(base64_decode($i[0]));
	}


} catch (Exception $ex) {

	// Ensure we close the VM Session if we hit a error, ensure we don't have a aborted VM
	if($vbox && $vbox->session && $vbox->session->handle) {
		try {
			$vbox->session->unlockMachine();
			unset($vbox->session);
		} catch (Exception $e) {
		}
	}

	if($_REQUEST['full'] && strpos($ex->faultstring,'VERR_NOT_SUPPORTED') > 0) {
		@header("Content-type: text/html");
		echo("Screen shots are not supported by your VirtualBox installation. To enable screen shots, please install a VirtualBox exteionsion pack that supports VRDE ");
		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>.");
	} else if($_REQUEST['full'] || $_REQUEST['debug']) {
		header("Content-type: text/html", true);
		echo("<pre>");
		print_r($ex);
		echo("</pre>");
	}
}

