1<?php 2/** 3 * 4 * Connects to vboxwebsrv, calls SOAP methods, and returns data. 5 * 6 * @author Ian Moore (imoore76 at yahoo dot com) 7 * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com) 8 * @version $Id: vboxconnector.php 599 2015-07-27 10:40:37Z imoore76 $ 9 * @package phpVirtualBox 10 * 11 */ 12 13class vboxconnector { 14 15 /** 16 * Error with safe HTML 17 * @var integer 18 */ 19 const PHPVB_ERRNO_HTML = 1; 20 21 /** 22 * Error number describing a fatal error 23 * @var integer 24 */ 25 const PHPVB_ERRNO_FATAL = 32; 26 27 /** 28 * Error number describing a connection error 29 * @var integer 30 */ 31 const PHPVB_ERRNO_CONNECT = 64; 32 33 /** 34 * phpVirtualBox groups extra value key 35 * @var string 36 */ 37 const phpVboxGroupKey = 'phpvb/Groups'; 38 39 /** 40 * Holds any errors that occur during processing. Errors are placed in here 41 * when we want calling functions to be aware of the error, but do not want to 42 * halt processing 43 * 44 * @var array 45 */ 46 var $errors = array(); 47 48 /** 49 * Holds any debug messages 50 * 51 * @var array 52 */ 53 var $messages = array(); 54 55 /** 56 * Settings object 57 * @var phpVBoxConfigClass 58 * @see phpVBoxConfigClass 59 */ 60 var $settings = null; 61 62 /** 63 * true if connected to vboxwebsrv 64 * @var boolean 65 */ 66 var $connected = false; 67 68 /** 69 * IVirtualBox instance 70 * @var IVirtualBox 71 */ 72 var $vbox = null; 73 74 /** 75 * VirtualBox web session manager 76 * @var IWebsessionManager 77 */ 78 var $websessionManager = null; 79 80 /** 81 * Holds IWebsessionManager session object if created 82 * during processing so that it can be properly shutdown 83 * in __destruct 84 * @var ISession 85 * @see vboxconnector::__destruct() 86 */ 87 var $session = null; 88 89 /** 90 * Holds VirtualBox version information 91 * @var array 92 */ 93 var $version = null; 94 95 /** 96 * If true, vboxconnector will not verify that there is a valid 97 * (PHP) session before connecting. 98 * @var boolean 99 */ 100 var $skipSessionCheck = false; 101 102 /** 103 * Holds items that should persist accross requests 104 * @var array 105 */ 106 var $persistentRequest = array(); 107 108 /** 109 * Holds VirtualBox host OS specific directory separator set by getDSep() 110 * @var string 111 * @see vboxconnector::getDsep() 112 */ 113 var $dsep = null; 114 115 /** 116 * Obtain configuration settings and set object vars 117 * @param boolean $useAuthMaster use the authentication master obtained from configuration class 118 * @see phpVBoxConfigClass 119 */ 120 public function __construct($useAuthMaster = false) { 121 122 require_once(dirname(__FILE__).'/language.php'); 123 require_once(dirname(__FILE__).'/vboxServiceWrappers.php'); 124 125 /* Set up.. .. settings */ 126 127 /** @var phpVBoxConfigClass */ 128 $this->settings = new phpVBoxConfigClass(); 129 130 // Are default settings being used? 131 if(@$this->settings->warnDefault) { 132 throw new Exception("No configuration found. Rename the file <b>config.php-example</b> in phpVirtualBox's folder to ". 133 "<b>config.php</b> and edit as needed.<p>For more detailed instructions, please see the installation wiki on ". 134 "phpVirtualBox's web site. <p><a href='https://github.com/phpvirtualbox/phpvirtualbox/wiki' target=_blank>". 135 "https://github.com/phpvirtualbox/phpvirtualbox/wiki</a>.</p>", 136 (vboxconnector::PHPVB_ERRNO_FATAL + vboxconnector::PHPVB_ERRNO_HTML)); 137 } 138 139 // Check for SoapClient class 140 if(!class_exists('SoapClient')) { 141 throw new Exception('PHP does not have the SOAP extension enabled.',vboxconnector::PHPVB_ERRNO_FATAL); 142 } 143 144 // use authentication master server? 145 if(@$useAuthMaster) { 146 $this->settings->setServer($this->settings->getServerAuthMaster()); 147 } 148 149 } 150 151 /** 152 * Connect to vboxwebsrv 153 * @see SoapClient 154 * @see phpVBoxConfigClass 155 * @return boolean true on success or if already connected 156 */ 157 public function connect() { 158 159 // Already connected? 160 if(@$this->connected) 161 return true; 162 163 // Valid session? 164 if(!@$this->skipSessionCheck && !$_SESSION['valid']) { 165 throw new Exception(trans('Not logged in.','UIUsers'),vboxconnector::PHPVB_ERRNO_FATAL); 166 } 167 168 // Persistent server? 169 if(@$this->persistentRequest['vboxServer']) { 170 $this->settings->setServer($this->persistentRequest['vboxServer']); 171 } 172 173 //Connect to webservice 174 $pvbxver = substr(@constant('PHPVBOX_VER'),0,(strpos(@constant('PHPVBOX_VER'),'-'))); 175 $this->client = new SoapClient(dirname(__FILE__)."/vboxwebService-".$pvbxver.".wsdl", 176 array( 177 'features' => (SOAP_USE_XSI_ARRAY_TYPE + SOAP_SINGLE_ELEMENT_ARRAYS), 178 'cache_wsdl' => WSDL_CACHE_BOTH, 179 'trace' => (@$this->settings->debugSoap), 180 'connection_timeout' => (@$this->settings->connectionTimeout ? $this->settings->connectionTimeout : 20), 181 'location' => @$this->settings->location 182 )); 183 184 185 // Persistent handles? 186 if(@$this->persistentRequest['vboxHandle']) { 187 188 try { 189 190 // Check for existing sessioin 191 $this->websessionManager = new IWebsessionManager($this->client); 192 $this->vbox = new IVirtualBox($this->client, $this->persistentRequest['vboxHandle']); 193 194 // force valid vbox check 195 $ev = $this->vbox->eventSource; 196 197 if($this->vbox->handle) 198 return ($this->connected = true); 199 200 201 } catch (Exception $e) { 202 // nothing. Fall through to new login. 203 204 } 205 } 206 207 /* Try / catch / throw here hides login credentials from exception if one is thrown */ 208 try { 209 $this->websessionManager = new IWebsessionManager($this->client); 210 $this->vbox = $this->websessionManager->logon($this->settings->username,$this->settings->password); 211 212 213 } catch (Exception $e) { 214 215 if(!($msg = $e->getMessage())) 216 $msg = 'Error logging in to vboxwebsrv.'; 217 else 218 $msg .= " ({$this->settings->location})"; 219 220 throw new Exception($msg,vboxconnector::PHPVB_ERRNO_CONNECT); 221 } 222 223 224 // Error logging in 225 if(!$this->vbox->handle) { 226 throw new Exception('Error logging in or connecting to vboxwebsrv.',vboxconnector::PHPVB_ERRNO_CONNECT); 227 } 228 229 // Hold handle 230 if(array_key_exists('vboxHandle',$this->persistentRequest)) { 231 $this->persistentRequest['vboxHandle'] = $this->vbox->handle; 232 } 233 234 return ($this->connected = true); 235 236 } 237 238 239 /** 240 * Get VirtualBox version 241 * @return array version information 242 */ 243 public function getVersion() { 244 245 if(!@$this->version) { 246 247 $this->connect(); 248 249 $this->version = explode('.',$this->vbox->version); 250 $this->version = array( 251 'ose' => (stripos($this->version[2],'ose') > 0), 252 'string' => join('.',$this->version), 253 'major' => intval(array_shift($this->version)), 254 'minor' => intval(array_shift($this->version)), 255 'sub' => intval(array_shift($this->version)), 256 'revision' => (string)$this->vbox->revision, 257 'settingsFilePath' => $this->vbox->settingsFilePath 258 ); 259 } 260 261 return $this->version; 262 263 } 264 265 /** 266 * 267 * Log out of vboxwebsrv 268 */ 269 public function __destruct() { 270 271 // Do not logout if there are persistent handles 272 if($this->connected && @$this->vbox->handle && !array_key_exists('vboxHandle' ,$this->persistentRequest)) { 273 274 // Failsafe to close session 275 if(@$this->session && @(string)$this->session->state == 'Locked') { 276 try {$this->session->unlockMachine();} 277 catch (Exception $e) { } 278 } 279 280 // Logoff 281 if($this->vbox->handle) 282 $this->websessionManager->logoff($this->vbox->handle); 283 284 } 285 286 unset($this->client); 287 } 288 289 /** 290 * Add a machine event listener to the listener list 291 * 292 * @param string $vm id of virtual machine to subscribe to 293 */ 294 private function _machineSubscribeEvents($vm) { 295 296 // Check for existing listener 297 if($this->persistentRequest['vboxEventListeners'][$vm]) { 298 299 try { 300 301 $listener = new IEventListener($this->client, $this->persistentRequest['vboxEventListeners'][$vm]['listener']); 302 $source = new IEventSource($this->client, $this->persistentRequest['vboxEventListeners'][$vm]['source']); 303 304 $source->unregisterListener($listener); 305 306 $listener->releaseRemote(); 307 $source->releaseRemote(); 308 309 } catch (Exception $e) { 310 // Pass 311 } 312 } 313 314 try { 315 316 /* @var $machine IMachine */ 317 $machine = $this->vbox->findMachine($vm); 318 319 /* Ignore if not running */ 320 $state = (string)$machine->state; 321 if($state != 'Running' && $state != 'Paused') { 322 $machine->releaseRemote(); 323 return; 324 } 325 326 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 327 $machine->lockMachine($this->session->handle, 'Shared'); 328 329 // Create and register event listener 330 $listener = $this->session->console->eventSource->createListener(); 331 $this->session->console->eventSource->registerListener($listener,array('Any'), false); 332 333 // Add to event listener list 334 $this->persistentRequest['vboxEventListeners'][$vm] = array( 335 'listener' => $listener->handle, 336 'source' => $this->session->console->eventSource->handle); 337 338 339 $machine->releaseRemote(); 340 341 } catch (Exception $e) { 342 // pass 343 } 344 345 if($this->session) { 346 try { 347 $this->session->unlockMachine(); 348 } catch (Exception $e) { 349 // pass 350 } 351 unset($this->session); 352 } 353 354 // Machine events before vbox events. This is in place to handle the "DrvVD_DEKMISSING" 355 // IRuntimeErrorEvent which tells us that a medium attached to a VM requires a password. 356 // This event needs to be presented to the client before the VM state change. This way 357 // the client can track whether or not the runtime error occurred in response to its 358 // startup request because the machine's RunTimeError will occur before vbox's 359 // StateChange. 360 uksort($this->persistentRequest['vboxEventListeners'], function($a, $b){ 361 if($a == 'vbox') return 1; 362 if($b == 'vbox') return -1; 363 return 0; 364 }); 365 366 } 367 368 /** 369 * Get pending vbox and machine events 370 * 371 * @param array $args array of arguments. See function body for details. 372 * @return array list of events 373 */ 374 public function remote_getEvents($args) { 375 376 $this->connect(); 377 378 $eventlist = array(); 379 380 // This should be an array 381 if(!is_array($this->persistentRequest['vboxEventListeners'])) { 382 383 $this->persistentRequest['vboxEventListeners'] = array(); 384 $listenerWait = 1000; 385 386 } else { 387 388 // The amount of time we will wait for events is determined by 389 // the amount of listeners - at least half a second 390 $listenerWait = max(100,intval(500/count($this->persistentRequest['vboxEventListeners']))); 391 } 392 393 // Get events from each configured event listener 394 foreach($this->persistentRequest['vboxEventListeners'] as $k => $el) { 395 396 try { 397 398 $listener = new IEventListener($this->client, $el['listener']); 399 $source = new IEventSource($this->client, $el['source']); 400 401 $event = $source->getEvent($listener,$listenerWait); 402 403 try { 404 405 while($event->handle) { 406 407 $eventData = $this->_getEventData($event, $k); 408 $source->eventProcessed($listener, $event); 409 $event->releaseRemote(); 410 411 412 // Only keep the last event of one particular type 413 //$eventlist[$eventData['dedupId']] = $eventData; 414 415 if($eventData) 416 $eventlist[$eventData['dedupId']] = $eventData; 417 418 $event = $source->getEvent($listener,100); 419 } 420 421 } catch (Exception $e) { 422 423 $this->errors[] = $e; 424 425 } 426 427 } catch (Exception $e) { 428 429 // Machine powered off or client has stale MO reference 430 if($listener) 431 try { $listener->releaseRemote(); } catch (Exceptoin $e) { 432 /// pass 433 } 434 if($source) 435 try { $source->releaseRemote(); } catch (Exceptoin $e) { 436 // pass 437 } 438 439 // Remove listener from list 440 unset($this->persistentRequest['vboxEventListeners'][$k]); 441 442 } 443 444 } 445 446 // Enrich events 447 foreach($eventlist as $k=>$event) { 448 449 switch($event['eventType']) { 450 451 /* Network adapter changed */ 452 case 'OnNetworkAdapterChanged': 453 454 try { 455 456 $machine = $this->vbox->findMachine($event['sourceId']); 457 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 458 459 // Session locked? 460 if((string)$this->session->state != 'Unlocked') 461 $this->session->unlockMachine(); 462 463 $machine->lockMachine($this->session->handle, 'Shared'); 464 465 try { 466 467 list($eventlist[$k]['enrichmentData']) = $this->_machineGetNetworkAdapters($this->session->machine, $event['networkAdapterSlot']); 468 469 } catch (Exception $e) { 470 // Just unlock the machine 471 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 472 } 473 474 $this->session->unlockMachine(); 475 $machine->releaseRemote(); 476 477 } catch (Exception $e) { 478 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 479 } 480 break; 481 482 483 /* VRDE server changed */ 484 case 'OnVRDEServerChanged': 485 try { 486 487 $machine = $this->vbox->findMachine($event['sourceId']); 488 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 489 490 // Session locked? 491 if((string)$this->session->state != 'Unlocked') 492 $this->session->unlockMachine(); 493 494 $machine->lockMachine($this->session->handle, 'Shared'); 495 $vrde = $this->session->machine->VRDEServer; 496 497 try { 498 $eventlist[$k]['enrichmentData'] = (!$vrde ? null : array( 499 'enabled' => $vrde->enabled, 500 'ports' => $vrde->getVRDEProperty('TCP/Ports'), 501 'netAddress' => $vrde->getVRDEProperty('TCP/Address'), 502 'VNCPassword' => $vrde->getVRDEProperty('VNCPassword'), 503 'authType' => (string)$vrde->authType, 504 'authTimeout' => $vrde->authTimeout 505 ) 506 ); 507 } catch (Exception $e) { 508 // Just unlock the machine 509 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 510 } 511 512 $this->session->unlockMachine(); 513 $machine->releaseRemote(); 514 515 } catch (Exception $e) { 516 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 517 } 518 break; 519 520 521 522 /* VRDE server info changed. Just need port and enabled/disabled */ 523 case 'OnVRDEServerInfoChanged': 524 try { 525 526 $machine = $this->vbox->findMachine($event['sourceId']); 527 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 528 529 // Session locked? 530 if((string)$this->session->state != 'Unlocked') 531 $this->session->unlockMachine(); 532 533 $machine->lockMachine($this->session->handle, 'Shared'); 534 535 try { 536 $eventlist[$k]['enrichmentData'] = array( 537 'port' => $this->session->console->VRDEServerInfo->port, 538 'enabled' => $this->session->machine->VRDEServer->enabled 539 ); 540 } catch (Exception $e) { 541 // Just unlock the machine 542 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 543 } 544 545 $this->session->unlockMachine(); 546 $machine->releaseRemote(); 547 548 } catch (Exception $e) { 549 $eventlist[$k]['enrichmentData'] = array($e->getMessage()); 550 } 551 break; 552 553 /* Machine registered */ 554 case 'OnMachineRegistered': 555 556 if(!$event['registered']) break; 557 558 // Get same data that is in VM list data 559 $vmdata = $this->remote_vboxGetMachines(array('vm'=>$event['machineId'])); 560 $eventlist[$k]['enrichmentData'] = $vmdata[0]; 561 unset($vmdata); 562 563 break; 564 565 /* enrich with basic machine data */ 566 case 'OnMachineDataChanged': 567 568 try { 569 570 $machine = $this->vbox->findMachine($event['machineId']); 571 572 if($this->settings->phpVboxGroups) { 573 $groups = explode(',',$machine->getExtraData(vboxconnector::phpVboxGroupKey)); 574 if(!is_array($groups) || (count($groups) == 1 && !$groups[0])) $groups = array("/"); 575 } else { 576 $groups = $machine->groups; 577 } 578 579 usort($groups, 'strnatcasecmp'); 580 581 $eventlist[$k]['enrichmentData'] = array( 582 'id' => $event['machineId'], 583 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $machine->name) : $machine->name, 584 'OSTypeId' => $machine->getOSTypeId(), 585 'owner' => (@$this->settings->enforceVMOwnership ? $machine->getExtraData("phpvb/sso/owner") : ''), 586 'groups' => $groups 587 ); 588 $machine->releaseRemote(); 589 590 } catch (Exception $e) { 591 // pass 592 } 593 break; 594 595 /* Update lastStateChange on OnMachineStateChange events */ 596 case 'OnMachineStateChanged': 597 try { 598 599 $machine = $this->vbox->findMachine($event['machineId']); 600 $eventlist[$k]['enrichmentData'] = array( 601 'lastStateChange' => (string)($machine->lastStateChange/1000), 602 'currentStateModified' => $machine->currentStateModified 603 ); 604 $machine->releaseRemote(); 605 606 } catch (Exception $e) { 607 $eventlist[$k]['enrichmentData'] = array('lastStateChange' => 0); 608 } 609 break; 610 611 /* enrich with snapshot name and new snapshot count*/ 612 case 'OnSnapshotTaken': 613 case 'OnSnapshotDeleted': 614 case 'OnSnapshotRestored': 615 case 'OnSnapshotChanged': 616 617 try { 618 $machine = $this->vbox->findMachine($event['machineId']); 619 $eventlist[$k]['enrichmentData'] = array( 620 'currentSnapshotName' => ($machine->currentSnapshot->handle ? $machine->currentSnapshot->name : ''), 621 'snapshotCount' => $machine->snapshotCount, 622 'currentStateModified' => $machine->currentStateModified 623 ); 624 $machine->releaseRemote(); 625 626 } catch (Exception $e) { 627 // pass 628 $this->errors[] = $e; 629 } 630 break; 631 632 } 633 634 } 635 636 return array_values($eventlist); 637 638 } 639 640 /** 641 * Subscribe to a single machine's events 642 * 643 * @param array $args array of arguments. See function body for details. 644 * @return boolean true on success 645 */ 646 public function remote_machineSubscribeEvents($args) { 647 648 $this->connect(); 649 foreach($args['vms'] as $vm) 650 $this->_machineSubscribeEvents($vm); 651 652 return true; 653 } 654 655 /** 656 * Unsubscribe from vbox and machine events 657 * 658 * @param array $args array of arguments. See function body for details. 659 * @return boolean true on success 660 */ 661 public function remote_unsubscribeEvents($args) { 662 663 $this->connect(); 664 665 if(!is_array($this->persistentRequest['vboxEventListeners'])) 666 $this->persistentRequest['vboxEventListeners'] = array(); 667 668 // Get events from each configured event listener 669 foreach($this->persistentRequest['vboxEventListeners'] as $k => $el) { 670 671 try { 672 673 $listener = new IEventListener($this->client, $el['listener']); 674 $source = new IEventSource($this->client, $el['source']); 675 676 $source->unregisterListener($listener); 677 678 $source->releaseRemote(); 679 $listener->releaseRemote(); 680 681 682 683 } catch (Exception $e) { 684 $this->errors[] = $e; 685 } 686 687 $this->persistentRequest['vboxEventListeners'][$k] = null; 688 689 } 690 691 $this->websessionManager->logoff($this->vbox->handle); 692 unset($this->vbox); 693 694 return true; 695 } 696 697 /** 698 * Subscribe to vbox and machine events 699 * 700 * @param array $args array of arguments. See function body for details. 701 * @return boolean true on success 702 */ 703 public function remote_subscribeEvents($args) { 704 705 $this->connect(); 706 707 // Check for existing listener 708 if($this->persistentRequest['vboxEventListeners']['vbox']) { 709 710 try { 711 712 $listener = new IEventListener($this->client, $this->persistentRequest['vboxEventListeners']['vbox']['listener']); 713 $source = new IEventSource($this->client, $this->persistentRequest['vboxEventListeners']['vbox']['source']); 714 715 $source->unregisterListener($listener); 716 717 $listener->releaseRemote(); 718 $source->releaseRemote(); 719 720 } catch (Exception $e) { 721 // Pass 722 } 723 } 724 725 // Create and register event listener 726 $listener = $this->vbox->eventSource->createListener(); 727 $this->vbox->eventSource->registerListener($listener,array('MachineEvent', 'SnapshotEvent', 'OnMediumRegistered', 'OnExtraDataChanged', 'OnSnapshotRestored'), false); 728 729 // Add to event listener list 730 $this->persistentRequest['vboxEventListeners']['vbox'] = array( 731 'listener' => $listener->handle, 732 'source' => $this->vbox->eventSource->handle); 733 734 // Subscribe to each machine in list 735 foreach($args['vms'] as $vm) { 736 $this->_machineSubscribeEvents($vm); 737 } 738 739 $this->persistentRequest['vboxHandle'] = $this->vbox->handle; 740 741 return true; 742 743 } 744 745 /** 746 * Return relevant event data for the event. 747 * 748 * @param IEvent $event 749 * @param String $listenerKey Key of event listener - 'vbox' or 750 * machine id 751 * @return array associative array of event attributes 752 */ 753 private function _getEventData($event, $listenerKey) { 754 755 $data = array('eventType'=>(string)$event->type,'sourceId'=>$listenerKey); 756 757 // Convert to parent class 758 $parentClass = 'I'.substr($data['eventType'],2).'Event'; 759 $eventDataObject = new $parentClass($this->client, $event->handle); 760 761 // Dedup ID is at least listener key ('vbox' or machine id) and event type 762 $data['dedupId'] = $listenerKey.'-'.$data['eventType']; 763 764 switch($data['eventType']) { 765 766 case 'OnMachineStateChanged': 767 $data['machineId'] = $eventDataObject->machineId; 768 $data['state'] = (string)$eventDataObject->state; 769 $data['dedupId'] .= '-'. $data['machineId']; 770 break; 771 772 case 'OnMachineDataChanged': 773 $data['machineId'] = $eventDataObject->machineId; 774 $data['dedupId'] .= '-'. $data['machineId']; 775 break; 776 777 case 'OnExtraDataCanChange': 778 case 'OnExtraDataChanged': 779 $data['machineId'] = $eventDataObject->machineId; 780 $data['key'] = $eventDataObject->key; 781 $data['value'] = $eventDataObject->value; 782 $data['dedupId'] .= '-'. $data['machineId'] .'-' . $data['key']; 783 break; 784 785 case 'OnMediumRegistered': 786 $data['machineId'] = $data['sourceId']; 787 $data['mediumId'] = $eventDataObject->mediumId; 788 $data['registered'] = $eventDataObject->registered; 789 $data['dedupId'] .= '-'. $data['mediumId']; 790 break; 791 792 case 'OnMachineRegistered': 793 $data['machineId'] = $eventDataObject->machineId; 794 $data['registered'] = $eventDataObject->registered; 795 $data['dedupId'] .= '-'. $data['machineId']; 796 break; 797 798 case 'OnSessionStateChanged': 799 $data['machineId'] = $eventDataObject->machineId; 800 $data['state'] = (string)$eventDataObject->state; 801 $data['dedupId'] .= '-'. $data['machineId']; 802 break; 803 804 /* Snapshot events */ 805 case 'OnSnapshotTaken': 806 case 'OnSnapshotDeleted': 807 case 'OnSnapshotRestored': 808 case 'OnSnapshotChanged': 809 $data['machineId'] = $eventDataObject->machineId; 810 $data['snapshotId'] = $eventDataObject->snapshotId; 811 $data['dedupId'] .= '-'. $data['machineId'] .'-' . $data['snapshotId']; 812 break; 813 814 case 'OnGuestPropertyChanged': 815 $data['machineId'] = $eventDataObject->machineId; 816 $data['name'] = $eventDataObject->name; 817 $data['value'] = $eventDataObject->value; 818 $data['flags'] = $eventDataObject->flags; 819 $data['dedupId'] .= '-'. $data['machineId'] .'-' . $data['name']; 820 break; 821 822 case 'OnCPUChanged': 823 $data['machineId'] = $data['sourceId']; 824 $data['cpu'] = $eventDataObject->cpu; 825 $data['add'] = $eventDataObject->add; 826 $data['dedupId'] .= '-' . $data['cpu']; 827 break; 828 829 /* Same end-result as network adapter changed */ 830 case 'OnNATRedirect': 831 $data['machineId'] = $data['sourceId']; 832 $data['eventType'] = 'OnNetworkAdapterChanged'; 833 $data['networkAdapterSlot'] = $eventDataObject->slot; 834 $data['dedupId'] = $listenerKey .'-OnNetworkAdapterChanged-'. $data['networkAdapterSlot']; 835 break; 836 837 case 'OnNetworkAdapterChanged': 838 $data['machineId'] = $data['sourceId']; 839 $data['networkAdapterSlot'] = $eventDataObject->networkAdapter->slot; 840 $data['dedupId'] .= '-'. $data['networkAdapterSlot']; 841 break; 842 843 /* Storage controller of VM changed */ 844 case 'OnStorageControllerChanged': 845 $data['machineId'] = $eventDataObject->machineId; 846 $data['dedupId'] .= '-'. $data['machineId']; 847 break; 848 849 /* Medium attachment changed */ 850 case 'OnMediumChanged': 851 $data['machineId'] = $data['sourceId']; 852 $ma = $eventDataObject->mediumAttachment; 853 $data['controller'] = $ma->controller; 854 $data['port'] = $ma->port; 855 $data['device'] = $ma->device; 856 try { 857 $data['medium'] = $ma->medium->id; 858 } catch (Exception $e) { 859 $data['medium'] = ''; 860 } 861 $data['dedupId'] .= '-'. $data['controller'] .'-'. $data['port'] .'-'.$data['device']; 862 break; 863 864 /* Generic machine changes that should query IMachine */ 865 case 'OnVRDEServerChanged': 866 $data['machineId'] = $data['sourceId']; 867 break; 868 case 'OnUSBControllerChanged': 869 $data['machineId'] = $data['sourceId']; 870 break; 871 case 'OnSharedFolderChanged': 872 $data['machineId'] = $data['sourceId']; 873 $data['scope'] = (string)$eventDataObject->scope; 874 break; 875 case 'OnVRDEServerInfoChanged': 876 $data['machineId'] = $data['sourceId']; 877 break; 878 case 'OnCPUExecutionCapChanged': 879 $data['machineId'] = $data['sourceId']; 880 $data['executionCap'] = $eventDataObject->executionCap; 881 break; 882 883 884 /* Notification when a USB device is attached to or detached from the virtual USB controller */ 885 case 'OnUSBDeviceStateChanged': 886 $data['machineId'] = $data['sourceId']; 887 $data['deviceId'] = $eventDataObject->device->id; 888 $data['attached'] = $eventDataObject->attached; 889 $data['dedupId'] .= '-'. $data['deviceId']; 890 break; 891 892 /* Machine execution error */ 893 case 'OnRuntimeError': 894 $data['id'] = (string)$eventDataObject->id; 895 $data['machineId'] = $data['sourceId']; 896 $data['message'] = $eventDataObject->message; 897 $data['fatal'] = $eventDataObject->fatal; 898 $data['dedupId'] .= '-' . $data['id']; 899 break; 900 901 /* Notification when a storage device is attached or removed. */ 902 case 'OnStorageDeviceChanged': 903 $data['machineId'] = $eventDataObject->machineId; 904 $data['storageDevice'] = $eventDataObject->storageDevice; 905 $data['removed'] = $eventDataObject->removed; 906 break; 907 908 /* On nat network delete / create */ 909 case 'OnNATNetworkCreationDeletion': 910 $data['creationEvent'] = $eventDataObject->creationEvent; 911 /* NAT network change */ 912 case 'OnNATNetworkSetting': 913 $data['networkName'] = $eventDataObject->networkName; 914 $data['dedupId'] .= '-' . $data['networkName']; 915 break; 916 917 default: 918 return null; 919 } 920 921 922 return $data; 923 924 } 925 926 927 /** 928 * Call overloader. 929 * Returns result of method call. Here is where python's decorators would come in handy. 930 * 931 * @param string $fn method to call 932 * @param array $args arguments for method 933 * @throws Exception 934 * @return array 935 */ 936 function __call($fn,$args) { 937 938 // Valid session? 939 global $_SESSION; 940 941 if(!@$this->skipSessionCheck && !$_SESSION['valid']) { 942 throw new Exception(trans('Not logged in.','UIUsers'),vboxconnector::PHPVB_ERRNO_FATAL); 943 } 944 945 $req = &$args[0]; 946 947 948 # Access to undefined methods prefixed with remote_ 949 if(method_exists($this,'remote_'.$fn)) { 950 951 $args[1][0]['data']['responseData'] = $this->{'remote_'.$fn}($req); 952 $args[1][0]['data']['success'] = ($args[1][0]['data']['responseData'] !== false); 953 $args[1][0]['data']['key'] = $this->settings->key; 954 955 // Not found 956 } else { 957 958 throw new Exception('Undefined method: ' . $fn ." - Clear your web browser's cache.",vboxconnector::PHPVB_ERRNO_FATAL); 959 960 } 961 962 return true; 963 } 964 965 /** 966 * Enumerate guest properties of a vm 967 * 968 * @param array $args array of arguments. See function body for details. 969 * @return array of guest properties 970 */ 971 public function remote_machineEnumerateGuestProperties($args) { 972 973 $this->connect(); 974 975 /* @var $m IMachine */ 976 $m = $this->vbox->findMachine($args['vm']); 977 978 $props = $m->enumerateGuestProperties($args['pattern']); 979 $m->releaseRemote(); 980 981 return $props; 982 983 } 984 985 /** 986 * Set extra data of a vm 987 * 988 * @param array $args array of arguments. See function body for details. 989 * @return array of extra data 990 */ 991 public function remote_machineSetExtraData($args) { 992 993 $this->connect(); 994 995 /* @var $m IMachine */ 996 $m = $this->vbox->findMachine($args['vm']); 997 998 $m->setExtraData($args['key'],$args['value']); 999 $m->releaseRemote(); 1000 1001 return true; 1002 1003 } 1004 1005 /** 1006 * Enumerate extra data of a vm 1007 * 1008 * @param array $args array of arguments. See function body for details. 1009 * @return array of extra data 1010 */ 1011 public function remote_machineEnumerateExtraData($args) { 1012 1013 $this->connect(); 1014 1015 /* @var $m IMachine */ 1016 $m = $this->vbox->findMachine($args['vm']); 1017 1018 $props = array(); 1019 1020 $keys = $m->getExtraDataKeys(); 1021 1022 usort($keys,'strnatcasecmp'); 1023 1024 foreach($keys as $k) { 1025 $props[$k] = $m->getExtraData($k); 1026 } 1027 $m->releaseRemote(); 1028 1029 return $props; 1030 1031 } 1032 1033 /** 1034 * Uses VirtualBox's vfsexplorer to check if a file exists 1035 * 1036 * @param array $args array of arguments. See function body for details. 1037 * @return boolean true if file exists 1038 */ 1039 public function remote_fileExists($args) { 1040 1041 /* No need to go through vfs explorer if local browser is true */ 1042 if($this->settings->browserLocal) { 1043 return file_exists($args['file']); 1044 } 1045 1046 $this->connect(); 1047 1048 $dsep = $this->getDsep(); 1049 1050 $path = str_replace($dsep.$dsep,$dsep,$args['file']); 1051 $dir = dirname($path); 1052 $file = basename($path); 1053 1054 if(substr($dir,-1) != $dsep) $dir .= $dsep; 1055 1056 /* @var $appl IAppliance */ 1057 $appl = $this->vbox->createAppliance(); 1058 1059 1060 /* @var $vfs IVFSExplorer */ 1061 $vfs = $appl->createVFSExplorer('file://'.$dir); 1062 1063 /* @var $progress IProgress */ 1064 $progress = $vfs->update(); 1065 $progress->waitForCompletion(-1); 1066 $progress->releaseRemote(); 1067 1068 $exists = $vfs->exists(array($file)); 1069 1070 $vfs->releaseRemote(); 1071 $appl->releaseRemote(); 1072 1073 1074 return count($exists); 1075 1076 } 1077 1078 /** 1079 * Install guest additions 1080 * 1081 * @param array $args array of arguments. See function body for details. 1082 * @return array result data 1083 */ 1084 public function remote_consoleGuestAdditionsInstall($args) { 1085 1086 $this->connect(); 1087 1088 $results = array('errored' => 0); 1089 1090 /* @var $gem IMedium|null */ 1091 $gem = null; 1092 foreach($this->vbox->DVDImages as $m) { /* @var $m IMedium */ 1093 if(strtolower($m->name) == 'vboxguestadditions.iso') { 1094 $gem = $m; 1095 break; 1096 } 1097 $m->releaseRemote(); 1098 } 1099 1100 // Not in media registry. Try to register it. 1101 if(!$gem) { 1102 $checks = array( 1103 'linux' => '/usr/share/virtualbox/VBoxGuestAdditions.iso', 1104 'osx' => '/Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso', 1105 'sunos' => '/opt/VirtualBox/additions/VBoxGuestAdditions.iso', 1106 'windows' => 'C:\Program Files\Oracle\VirtualBox\VBoxGuestAdditions.iso', 1107 'windowsx86' => 'C:\Program Files (x86)\Oracle\VirtualBox\VBoxGuestAdditions.iso' // Does this exist? 1108 ); 1109 $hostos = $this->vbox->host->operatingSystem; 1110 if(stripos($hostos,'windows') !== false) { 1111 $checks = array($checks['windows'],$checks['windowsx86']); 1112 } elseif(stripos($hostos,'solaris') !== false || stripos($hostos,'sunos') !== false) { 1113 $checks = array($checks['sunos']); 1114 // not sure of uname returned on Mac. This should cover all of them 1115 } elseif(stripos($hostos,'mac') !== false || stripos($hostos,'apple') !== false || stripos($hostos,'osx') !== false || stripos($hostos,'os x') !== false || stripos($hostos,'darwin') !== false) { 1116 $checks = array($checks['osx']); 1117 } elseif(stripos($hostos,'linux') !== false) { 1118 $checks = array($checks['linux']); 1119 } 1120 1121 // Check for config setting 1122 if(@$this->settings->vboxGuestAdditionsISO) 1123 $checks = array($this->settings->vboxGuestAdditionsISO); 1124 1125 // Unknown os and no config setting leaves all checks in place. 1126 // Try to register medium. 1127 foreach($checks as $iso) { 1128 try { 1129 $gem = $this->vbox->openMedium($iso,'DVD','ReadOnly',false); 1130 break; 1131 } catch (Exception $e) { 1132 // Ignore 1133 } 1134 } 1135 $results['sources'] = $checks; 1136 } 1137 1138 // No guest additions found 1139 if(!$gem) { 1140 $results['result'] = 'noadditions'; 1141 return $results; 1142 } 1143 1144 // create session and lock machine 1145 /* @var $machine IMachine */ 1146 $machine = $this->vbox->findMachine($args['vm']); 1147 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1148 $machine->lockMachine($this->session->handle, 'Shared'); 1149 1150 // Try update from guest if it is supported 1151 if(!@$args['mount_only']) { 1152 try { 1153 1154 /* @var $progress IProgress */ 1155 $progress = $this->session->console->guest->updateGuestAdditions($gem->location,array(),'WaitForUpdateStartOnly'); 1156 1157 // No error info. Save progress. 1158 $gem->releaseRemote(); 1159 $this->_util_progressStore($progress); 1160 $results['progress'] = $progress->handle; 1161 return $results; 1162 1163 } catch (Exception $e) { 1164 1165 if(!empty($results['progress'])) 1166 unset($results['progress']); 1167 1168 // Try to mount medium 1169 $results['errored'] = 1; 1170 } 1171 } 1172 1173 // updateGuestAdditions is not supported. Just try to mount image. 1174 $results['result'] = 'nocdrom'; 1175 $mounted = false; 1176 foreach($machine->storageControllers as $sc) { /* @var $sc IStorageController */ 1177 foreach($machine->getMediumAttachmentsOfController($sc->name) as $ma) { /* @var $ma IMediumAttachment */ 1178 if((string)$ma->type == 'DVD') { 1179 $this->session->machine->mountMedium($sc->name, $ma->port, $ma->device, $gem->handle, true); 1180 $results['result'] = 'mounted'; 1181 $mounted = true; 1182 break; 1183 } 1184 } 1185 $sc->releaseRemote(); 1186 if($mounted) break; 1187 } 1188 1189 1190 $this->session->unlockMachine(); 1191 unset($this->session); 1192 $machine->releaseRemote(); 1193 $gem->releaseRemote(); 1194 1195 return $results; 1196 } 1197 1198 /** 1199 * Attach USB device identified by $args['id'] to a running VM 1200 * 1201 * @param array $args array of arguments. See function body for details. 1202 * @return boolean true on success 1203 */ 1204 public function remote_consoleUSBDeviceAttach($args) { 1205 1206 $this->connect(); 1207 1208 // create session and lock machine 1209 /* @var $machine IMachine */ 1210 $machine = $this->vbox->findMachine($args['vm']); 1211 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1212 $machine->lockMachine($this->session->handle, 'Shared'); 1213 1214 $this->session->console->attachUSBDevice($args['id']); 1215 1216 $this->session->unlockMachine(); 1217 unset($this->session); 1218 $machine->releaseRemote(); 1219 1220 return true; 1221 } 1222 1223 /** 1224 * Detach USB device identified by $args['id'] from a running VM 1225 * 1226 * @param array $args array of arguments. See function body for details. 1227 * @return boolean true on success 1228 */ 1229 public function remote_consoleUSBDeviceDetach($args) { 1230 1231 $this->connect(); 1232 1233 // create session and lock machine 1234 /* @var $machine IMachine */ 1235 $machine = $this->vbox->findMachine($args['vm']); 1236 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1237 $machine->lockMachine($this->session->handle, 'Shared'); 1238 1239 $this->session->console->detachUSBDevice($args['id']); 1240 1241 $this->session->unlockMachine(); 1242 unset($this->session); 1243 $machine->releaseRemote(); 1244 1245 return true; 1246 } 1247 1248 /** 1249 * Save vms' groups if they have changed 1250 * 1251 * @param array $args array of arguments. See function body for details. 1252 * @return array response data 1253 */ 1254 public function remote_machinesSaveGroups($args) { 1255 1256 $this->connect(); 1257 1258 $response = array('saved'=>array(),'errored'=>false); 1259 1260 foreach($args['vms'] as $vm) { 1261 1262 // create session and lock machine 1263 /* @var $machine IMachine */ 1264 try { 1265 $machine = $this->vbox->findMachine($vm['id']); 1266 } catch (Exception $null) { 1267 continue; 1268 } 1269 1270 $newGroups = $vm['groups']; 1271 1272 if($this->settings->phpVboxGroups) { 1273 1274 $oldGroups = explode(',',$machine->getExtraData(vboxconnector::phpVboxGroupKey)); 1275 if(!is_array($oldGroups)) $oldGroups = array("/"); 1276 if(!count(array_diff($oldGroups,$newGroups)) && !count(array_diff($newGroups,$oldGroups))) { 1277 continue; 1278 } 1279 1280 } else { 1281 1282 $oldGroups = $machine->groups; 1283 1284 if((string)$machine->sessionState != 'Unlocked' || (!count(array_diff($oldGroups,$newGroups)) && !count(array_diff($newGroups,$oldGroups)))) { 1285 $machine->releaseRemote(); 1286 continue; 1287 } 1288 1289 } 1290 1291 try { 1292 1293 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1294 1295 $machine->lockMachine($this->session->handle, 'Shared'); 1296 1297 usort($newGroups,'strnatcasecmp'); 1298 1299 if($this->settings->phpVboxGroups) { 1300 $this->session->machine->setExtraData(vboxconnector::phpVboxGroupKey, implode(',', $newGroups)); 1301 } else { 1302 $this->session->machine->groups = $newGroups; 1303 } 1304 1305 $this->session->machine->saveSettings(); 1306 $this->session->unlockMachine(); 1307 1308 unset($this->session); 1309 $machine->releaseRemote(); 1310 1311 } catch (Exception $e) { 1312 1313 $this->errors[] = $e; 1314 $response['errored'] = true; 1315 1316 try { 1317 $this->session->unlockMachine(); 1318 unset($this->session); 1319 } catch (Exception $e) { 1320 // pass 1321 } 1322 1323 continue; 1324 1325 } 1326 1327 // Add to saved list 1328 $response['saved'][] = $vm['id']; 1329 1330 } 1331 1332 1333 return $response; 1334 1335 1336 } 1337 1338 1339 /** 1340 * Clone a virtual machine 1341 * 1342 * @param array $args array of arguments. See function body for details. 1343 * @return array response data 1344 */ 1345 public function remote_machineClone($args) { 1346 1347 // Connect to vboxwebsrv 1348 $this->connect(); 1349 1350 /* @var $src IMachine */ 1351 $src = $this->vbox->findMachine($args['src']); 1352 1353 if($args['snapshot'] && $args['snapshot']['id']) { 1354 /* @var $nsrc ISnapshot */ 1355 $nsrc = $src->findSnapshot($args['snapshot']['id']); 1356 $src->releaseRemote(); 1357 $src = null; 1358 $src = $nsrc->machine; 1359 } 1360 /* @var $m IMachine */ 1361 $m = $this->vbox->createMachine($this->vbox->composeMachineFilename($args['name'],null,null,null),$args['name'],null,null,null,false); 1362 $sfpath = $m->settingsFilePath; 1363 1364 /* @var $cm CloneMode */ 1365 $cm = new CloneMode(null,$args['vmState']); 1366 $state = $cm->ValueMap[$args['vmState']]; 1367 1368 1369 $opts = array(); 1370 if(!$args['reinitNetwork']) $opts[] = 'KeepAllMACs'; 1371 if($args['link']) $opts[] = 'Link'; 1372 1373 /* @var $progress IProgress */ 1374 $progress = $src->cloneTo($m->handle,$args['vmState'],$opts); 1375 1376 // Does an exception exist? 1377 try { 1378 if($progress->errorInfo->handle) { 1379 $this->errors[] = new Exception($progress->errorInfo->text); 1380 $progress->releaseRemote(); 1381 return false; 1382 } 1383 } catch (Exception $null) {} 1384 1385 $m->releaseRemote(); 1386 $src->releaseRemote(); 1387 1388 $this->_util_progressStore($progress); 1389 1390 return array( 1391 'progress' => $progress->handle, 1392 'settingsFilePath' => $sfpath); 1393 1394 } 1395 1396 1397 /** 1398 * Turn VRDE on / off on a running VM 1399 * 1400 * @param array $args array of arguments. See function body for details. 1401 * @return boolean true on success 1402 */ 1403 public function remote_consoleVRDEServerSave($args) { 1404 1405 $this->connect(); 1406 1407 // create session and lock machine 1408 /* @var $m IMachine */ 1409 $m = $this->vbox->findMachine($args['vm']); 1410 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1411 $m->lockMachine($this->session->handle, 'Shared'); 1412 1413 if(intval($args['enabled']) == -1) { 1414 $args['enabled'] = intval(!$this->session->machine->VRDEServer->enabled); 1415 } 1416 1417 $this->session->machine->VRDEServer->enabled = intval($args['enabled']); 1418 1419 $this->session->unlockMachine(); 1420 unset($this->session); 1421 1422 $m->releaseRemote(); 1423 1424 return true; 1425 } 1426 1427 /** 1428 * Save running VM settings. Called from machineSave method if the requested VM is running. 1429 * 1430 * @param array $args array of machine configuration items. 1431 * @param string $state state of virtual machine. 1432 * @return boolean true on success 1433 */ 1434 private function _machineSaveRunning($args, $state) { 1435 1436 // Client and server must agree on advanced config setting 1437 $this->settings->enableAdvancedConfig = (@$this->settings->enableAdvancedConfig && @$args['clientConfig']['enableAdvancedConfig']); 1438 $this->settings->enableHDFlushConfig = (@$this->settings->enableHDFlushConfig && @$args['clientConfig']['enableHDFlushConfig']); 1439 1440 // Shorthand 1441 /* @var $m IMachine */ 1442 $m = &$this->session->machine; 1443 1444 $m->CPUExecutionCap = $args['CPUExecutionCap']; 1445 $m->description = $args['description']; 1446 1447 // Start / stop config 1448 if(@$this->settings->startStopConfig) { 1449 $m->setExtraData('pvbx/startupMode', $args['startupMode']); 1450 } 1451 1452 // VirtualBox style start / stop config 1453 if(@$this->settings->vboxAutostartConfig && @$args['clientConfig']['vboxAutostartConfig']) { 1454 1455 $m->autostopType = $args['autostopType']; 1456 $m->autostartEnabled = $args['autostartEnabled']; 1457 $m->autostartDelay = $args['autostartDelay']; 1458 1459 } 1460 1461 // Custom Icon 1462 if(@$this->settings->enableCustomIcons) { 1463 $m->setExtraData('phpvb/icon', $args['customIcon']); 1464 } 1465 1466 // VRDE settings 1467 try { 1468 if($m->VRDEServer && $this->vbox->systemProperties->defaultVRDEExtPack) { 1469 $m->VRDEServer->enabled = $args['VRDEServer']['enabled']; 1470 $m->VRDEServer->setVRDEProperty('TCP/Ports',$args['VRDEServer']['ports']); 1471 $m->VRDEServer->setVRDEProperty('VNCPassword',$args['VRDEServer']['VNCPassword'] ? $args['VRDEServer']['VNCPassword'] : null); 1472 $m->VRDEServer->authType = ($args['VRDEServer']['authType'] ? $args['VRDEServer']['authType'] : null); 1473 $m->VRDEServer->authTimeout = $args['VRDEServer']['authTimeout']; 1474 } 1475 } catch (Exception $e) { 1476 } 1477 1478 // Storage Controllers if machine is in a valid state 1479 if($state != 'Saved') { 1480 1481 $scs = $m->storageControllers; 1482 $attachedEx = $attachedNew = array(); 1483 foreach($scs as $sc) { /* @var $sc IStorageController */ 1484 $mas = $m->getMediumAttachmentsOfController($sc->name); 1485 foreach($mas as $ma) { /* @var $ma IMediumAttachment */ 1486 $attachedEx[$sc->name.$ma->port.$ma->device] = (($ma->medium->handle && $ma->medium->id) ? $ma->medium->id : null); 1487 } 1488 } 1489 1490 // Incoming list 1491 foreach($args['storageControllers'] as $sc) { 1492 1493 $sc['name'] = trim($sc['name']); 1494 $name = ($sc['name'] ? $sc['name'] : $sc['bus']); 1495 1496 // Medium attachments 1497 foreach($sc['mediumAttachments'] as $ma) { 1498 1499 if($ma['medium'] == 'null') $ma['medium'] = null; 1500 1501 $attachedNew[$name.$ma['port'].$ma['device']] = $ma['medium']['id']; 1502 1503 // Compare incoming list with existing 1504 if($ma['type'] != 'HardDisk' && $attachedNew[$name.$ma['port'].$ma['device']] != $attachedEx[$name.$ma['port'].$ma['device']]) { 1505 1506 if(is_array($ma['medium']) && $ma['medium']['id'] && $ma['type']) { 1507 1508 // Host drive 1509 if(strtolower($ma['medium']['hostDrive']) == 'true' || $ma['medium']['hostDrive'] === true) { 1510 // CD / DVD Drive 1511 if($ma['type'] == 'DVD') { 1512 $drives = $this->vbox->host->DVDDrives; 1513 // floppy drives 1514 } else { 1515 $drives = $this->vbox->host->floppyDrives; 1516 } 1517 foreach($drives as $md) { 1518 if($md->id == $ma['medium']['id']) { 1519 $med = &$md; 1520 break; 1521 } 1522 $md->releaseRemote(); 1523 } 1524 } else { 1525 $med = $this->vbox->openMedium($ma['medium']['location'],$ma['type'],'ReadWrite',false); 1526 } 1527 } else { 1528 $med = null; 1529 } 1530 $m->mountMedium($name,$ma['port'],$ma['device'],(is_object($med) ? $med->handle : null),true); 1531 if(is_object($med)) $med->releaseRemote(); 1532 } 1533 1534 // Set Live CD/DVD 1535 if($ma['type'] == 'DVD') { 1536 if(!$ma['medium']['hostDrive']) 1537 $m->temporaryEjectDevice($name, $ma['port'], $ma['device'], $ma['temporaryEject']); 1538 1539 // Set IgnoreFlush 1540 } elseif($ma['type'] == 'HardDisk') { 1541 1542 // Remove IgnoreFlush key? 1543 if($this->settings->enableHDFlushConfig) { 1544 1545 $xtra = $this->_util_getIgnoreFlushKey($ma['port'], $ma['device'], $sc['controllerType']); 1546 1547 if($xtra) { 1548 if((bool)($ma['ignoreFlush'])) { 1549 $m->setExtraData($xtra, '0'); 1550 } else { 1551 $m->setExtraData($xtra, ''); 1552 } 1553 } 1554 } 1555 1556 1557 } 1558 } 1559 1560 } 1561 } 1562 1563 1564 /* Networking */ 1565 $netprops = array('enabled','attachmentType','bridgedInterface','hostOnlyInterface','internalNetwork','NATNetwork','promiscModePolicy','genericDriver'); 1566 if(@$this->settings->enableVDE) $netprops[] = 'VDENetwork'; 1567 1568 for($i = 0; $i < count($args['networkAdapters']); $i++) { 1569 1570 /* @var $n INetworkAdapter */ 1571 $n = $m->getNetworkAdapter($i); 1572 1573 // Skip disabled adapters 1574 if(!$n->enabled) { 1575 $n->releaseRemote(); 1576 continue; 1577 } 1578 1579 for($p = 0; $p < count($netprops); $p++) { 1580 switch($netprops[$p]) { 1581 case 'enabled': 1582 case 'cableConnected': 1583 break; 1584 default: 1585 if((string)$n->{$netprops[$p]} != (string)$args['networkAdapters'][$i][$netprops[$p]]) 1586 $n->{$netprops[$p]} = $args['networkAdapters'][$i][$netprops[$p]]; 1587 } 1588 } 1589 1590 /// Not if in "Saved" state 1591 if($state != 'Saved') { 1592 1593 // Network properties 1594 $eprops = $n->getProperties(null); 1595 $eprops = array_combine($eprops[1],$eprops[0]); 1596 $iprops = array_map(function($a){$b=explode("=",$a); return array($b[0]=>$b[1]);},preg_split('/[\r|\n]+/',$args['networkAdapters'][$i]['properties'])); 1597 $inprops = array(); 1598 foreach($iprops as $a) { 1599 foreach($a as $k=>$v) 1600 $inprops[$k] = $v; 1601 } 1602 1603 // Remove any props that are in the existing properties array 1604 // but not in the incoming properties array 1605 foreach(array_diff(array_keys($eprops),array_keys($inprops)) as $dk) { 1606 $n->setProperty($dk, ''); 1607 } 1608 1609 // Set remaining properties 1610 foreach($inprops as $k => $v) { 1611 if(!$k) continue; 1612 $n->setProperty($k, $v); 1613 } 1614 1615 if($n->cableConnected != $args['networkAdapters'][$i]['cableConnected']) 1616 $n->cableConnected = $args['networkAdapters'][$i]['cableConnected']; 1617 1618 } 1619 1620 if($args['networkAdapters'][$i]['attachmentType'] == 'NAT') { 1621 1622 // Remove existing redirects 1623 foreach($n->NATEngine->getRedirects() as $r) { 1624 $n->NATEngine->removeRedirect(array_shift(explode(',',$r))); 1625 } 1626 // Add redirects 1627 foreach($args['networkAdapters'][$i]['redirects'] as $r) { 1628 $r = explode(',',$r); 1629 $n->NATEngine->addRedirect($r[0],$r[1],$r[2],$r[3],$r[4],$r[5]); 1630 } 1631 1632 // Advanced NAT settings 1633 if($state != 'Saved' && @$this->settings->enableAdvancedConfig) { 1634 $aliasMode = $n->NATEngine->aliasMode & 1; 1635 if(intval($args['networkAdapters'][$i]['NATEngine']['aliasMode'] & 2)) $aliasMode |= 2; 1636 if(intval($args['networkAdapters'][$i]['NATEngine']['aliasMode'] & 4)) $aliasMode |= 4; 1637 $n->NATEngine->aliasMode = $aliasMode; 1638 $n->NATEngine->DNSProxy = $args['networkAdapters'][$i]['NATEngine']['DNSProxy']; 1639 $n->NATEngine->DNSPassDomain = $args['networkAdapters'][$i]['NATEngine']['DNSPassDomain']; 1640 $n->NATEngine->DNSUseHostResolver = $args['networkAdapters'][$i]['NATEngine']['DNSUseHostResolver']; 1641 $n->NATEngine->hostIP = $args['networkAdapters'][$i]['NATEngine']['hostIP']; 1642 } 1643 1644 } else if($args['networkAdapters'][$i]['attachmentType'] == 'NATNetwork') { 1645 1646 if($n->NATNetwork = $args['networkAdapters'][$i]['NATNetwork']); 1647 } 1648 1649 $n->releaseRemote(); 1650 1651 } 1652 1653 /* Shared Folders */ 1654 $sf_inc = array(); 1655 foreach($args['sharedFolders'] as $s) { 1656 $sf_inc[$s['name']] = $s; 1657 } 1658 1659 1660 // Get list of perm shared folders 1661 $psf_tmp = $m->sharedFolders; 1662 $psf = array(); 1663 foreach($psf_tmp as $sf) { 1664 $psf[$sf->name] = $sf; 1665 } 1666 1667 // Get a list of temp shared folders 1668 $tsf_tmp = $this->session->console->sharedFolders; 1669 $tsf = array(); 1670 foreach($tsf_tmp as $sf) { 1671 $tsf[$sf->name] = $sf; 1672 } 1673 1674 /* 1675 * Step through list and remove non-matching folders 1676 */ 1677 foreach($sf_inc as $sf) { 1678 1679 // Already exists in perm list. Check Settings. 1680 if($sf['type'] == 'machine' && $psf[$sf['name']]) { 1681 1682 /* Remove if it doesn't match */ 1683 if($sf['hostPath'] != $psf[$sf['name']]->hostPath || (bool)$sf['autoMount'] != (bool)$psf[$sf['name']]->autoMount || (bool)$sf['writable'] != (bool)$psf[$sf['name']]->writable) { 1684 1685 $m->removeSharedFolder($sf['name']); 1686 $m->createSharedFolder($sf['name'],$sf['hostPath'],(bool)$sf['writable'],(bool)$sf['autoMount']); 1687 } 1688 1689 unset($psf[$sf['name']]); 1690 1691 // Already exists in perm list. Check Settings. 1692 } else if($sf['type'] != 'machine' && $tsf[$sf['name']]) { 1693 1694 /* Remove if it doesn't match */ 1695 if($sf['hostPath'] != $tsf[$sf['name']]->hostPath || (bool)$sf['autoMount'] != (bool)$tsf[$sf['name']]->autoMount || (bool)$sf['writable'] != (bool)$tsf[$sf['name']]->writable) { 1696 1697 $this->session->console->removeSharedFolder($sf['name']); 1698 $this->session->console->createSharedFolder($sf['name'],$sf['hostPath'],(bool)$sf['writable'],(bool)$sf['autoMount']); 1699 1700 } 1701 1702 unset($tsf[$sf['name']]); 1703 1704 } else { 1705 1706 // Does not exist or was removed. Add it. 1707 if($sf['type'] != 'machine') $this->session->console->createSharedFolder($sf['name'],$sf['hostPath'],(bool)$sf['writable'],(bool)$sf['autoMount']); 1708 else $this->session->machine->createSharedFolder($sf['name'],$sf['hostPath'],(bool)$sf['writable'],(bool)$sf['autoMount']); 1709 } 1710 1711 } 1712 1713 /* 1714 * Remove remaining 1715 */ 1716 foreach($psf as $sf) $m->removeSharedFolder($sf->name); 1717 foreach($tsf as $sf) $this->session->console->removeSharedFolder($sf->name); 1718 1719 /* 1720 * USB Filters 1721 */ 1722 1723 $usbEx = array(); 1724 $usbNew = array(); 1725 1726 $usbc = $this->_machineGetUSBControllers($this->session->machine); 1727 1728 $deviceFilters = $this->_machineGetUSBDeviceFilters($this->session->machine); 1729 1730 if($state != 'Saved') { 1731 1732 // filters 1733 if(!is_array($args['USBDeviceFilters'])) $args['USBDeviceFilters'] = array(); 1734 1735 if(count($deviceFilters) != count($args['USBDeviceFilters']) || @serialize($deviceFilters) != @serialize($args['USBDeviceFilters'])) { 1736 1737 // usb filter properties to change 1738 $usbProps = array('vendorId','productId','revision','manufacturer','product','serialNumber','port','remote'); 1739 1740 // Remove and Add filters 1741 try { 1742 1743 1744 $max = max(count($deviceFilters),count($args['USBDeviceFilters'])); 1745 $offset = 0; 1746 1747 // Remove existing 1748 for($i = 0; $i < $max; $i++) { 1749 1750 // Only if filter differs 1751 if(@serialize($deviceFilters[$i]) != @serialize($args['USBDeviceFilters'][$i])) { 1752 1753 // Remove existing? 1754 if($i < count($deviceFilters)) { 1755 $m->USBDeviceFilters->removeDeviceFilter(($i-$offset)); 1756 $offset++; 1757 } 1758 1759 // Exists in new? 1760 if(count($args['USBDeviceFilters'][$i])) { 1761 1762 // Create filter 1763 $f = $m->USBDeviceFilters->createDeviceFilter($args['USBDeviceFilters'][$i]['name']); 1764 $f->active = (bool)$args['USBDeviceFilters'][$i]['active']; 1765 1766 foreach($usbProps as $p) { 1767 $f->$p = $args['USBDeviceFilters'][$i][$p]; 1768 } 1769 1770 $m->USBDeviceFilters->insertDeviceFilter($i,$f->handle); 1771 $f->releaseRemote(); 1772 $offset--; 1773 } 1774 } 1775 1776 } 1777 1778 } catch (Exception $e) { $this->errors[] = $e; } 1779 1780 } 1781 1782 } 1783 1784 1785 $this->session->machine->saveSettings(); 1786 $this->session->unlockMachine(); 1787 unset($this->session); 1788 $m->releaseRemote(); 1789 1790 return true; 1791 1792 } 1793 1794 /** 1795 * Save virtual machine settings. 1796 * 1797 * @param array $args array of arguments. See function body for details. 1798 * @return boolean true on success 1799 */ 1800 public function remote_machineSave($args) { 1801 1802 $this->connect(); 1803 1804 // create session and lock machine 1805 /* @var $machine IMachine */ 1806 $machine = $this->vbox->findMachine($args['id']); 1807 1808 $vmState = (string)$machine->state; 1809 $vmRunning = ($vmState == 'Running' || $vmState == 'Paused' || $vmState == 'Saved'); 1810 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 1811 $machine->lockMachine($this->session->handle, ($vmRunning ? 'Shared' : 'Write')); 1812 1813 // Switch to machineSaveRunning()? 1814 if($vmRunning) { 1815 return $this->_machineSaveRunning($args, $vmState); 1816 } 1817 1818 1819 // Client and server must agree on advanced config setting 1820 $this->settings->enableAdvancedConfig = (@$this->settings->enableAdvancedConfig && @$args['clientConfig']['enableAdvancedConfig']); 1821 $this->settings->enableHDFlushConfig = (@$this->settings->enableHDFlushConfig && @$args['clientConfig']['enableHDFlushConfig']); 1822 1823 // Shorthand 1824 /* @var $m IMachine */ 1825 $m = $this->session->machine; 1826 1827 // General machine settings 1828 if (@$this->settings->enforceVMOwnership ) { 1829 1830 $args['name'] = "{$_SESSION['user']}_" . preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $args['name']); 1831 1832 if ( ($owner = $machine->getExtraData("phpvb/sso/owner")) && $owner !== $_SESSION['user'] && !$_SESSION['admin'] ) 1833 { 1834 // skip this VM as it is not owned by the user we're logged in as 1835 throw new Exception("Not authorized to modify this VM"); 1836 } 1837 1838 } 1839 1840 // Change OS type and update LongMode 1841 if(strcasecmp($m->OSTypeId,$args['OSTypeId']) != 0) { 1842 1843 $m->OSTypeId = $args['OSTypeId']; 1844 1845 $guestOS = $this->vbox->getGuestOSType($args['OSTypeId']); 1846 1847 $m->setCPUProperty('LongMode', ($guestOS->is64Bit ? 1 : 0)); 1848 } 1849 1850 $m->CPUCount = $args['CPUCount']; 1851 $m->memorySize = $args['memorySize']; 1852 $m->firmwareType = $args['firmwareType']; 1853 if($args['chipsetType']) $m->chipsetType = $args['chipsetType']; 1854 if($m->snapshotFolder != $args['snapshotFolder']) $m->snapshotFolder = $args['snapshotFolder']; 1855 $m->RTCUseUTC = ($args['RTCUseUTC'] ? 1 : 0); 1856 $m->setCpuProperty('PAE', ($args['CpuProperties']['PAE'] ? 1 : 0)); 1857 $m->setCPUProperty('LongMode', (strpos($args['OSTypeId'],'_64') > - 1 ? 1 : 0)); 1858 1859 // IOAPIC 1860 $m->BIOSSettings->IOAPICEnabled = ($args['BIOSSettings']['IOAPICEnabled'] ? 1 : 0); 1861 $m->CPUExecutionCap = $args['CPUExecutionCap']; 1862 $m->description = $args['description']; 1863 1864 // Start / stop config 1865 if(@$this->settings->startStopConfig) { 1866 $m->setExtraData('pvbx/startupMode', $args['startupMode']); 1867 } 1868 1869 1870 // VirtualBox style start / stop config 1871 if(@$this->settings->vboxAutostartConfig && @$args['clientConfig']['vboxAutostartConfig']) { 1872 1873 $m->autostopType = $args['autostopType']; 1874 $m->autostartEnabled = $args['autostartEnabled']; 1875 $m->autostartDelay = $args['autostartDelay']; 1876 1877 } 1878 1879 // Determine if host is capable of hw accel 1880 $hwAccelAvail = $this->vbox->host->getProcessorFeature('HWVirtEx'); 1881 1882 $m->paravirtProvider = $args['paravirtProvider']; 1883 $m->setHWVirtExProperty('Enabled', $args['HWVirtExProperties']['Enabled']); 1884 $m->setHWVirtExProperty('NestedPaging', ($args['HWVirtExProperties']['Enabled'] && $hwAccelAvail && $args['HWVirtExProperties']['NestedPaging'])); 1885 1886 /* Only if advanced configuration is enabled */ 1887 if(@$this->settings->enableAdvancedConfig) { 1888 1889 /** @def VBOX_WITH_PAGE_SHARING 1890 * Enables the page sharing code. 1891 * @remarks This must match GMMR0Init; currently we only support page fusion on 1892 * all 64-bit hosts except Mac OS X */ 1893 1894 if($this->vbox->host->getProcessorFeature('LongMode')) { 1895 1896 $m->pageFusionEnabled = $args['pageFusionEnabled']; 1897 } 1898 1899 $m->HPETEnabled = $args['HPETEnabled']; 1900 $m->setExtraData("VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", $args['disableHostTimeSync']); 1901 $m->keyboardHIDType = $args['keyboardHIDType']; 1902 $m->pointingHIDType = $args['pointingHIDType']; 1903 $m->setHWVirtExProperty('LargePages', $args['HWVirtExProperties']['LargePages']); 1904 $m->setHWVirtExProperty('UnrestrictedExecution', $args['HWVirtExProperties']['UnrestrictedExecution']); 1905 $m->setHWVirtExProperty('VPID', $args['HWVirtExProperties']['VPID']); 1906 1907 } 1908 1909 /* Custom Icon */ 1910 if(@$this->settings->enableCustomIcons) 1911 $m->setExtraData('phpvb/icon', $args['customIcon']); 1912 1913 $m->VRAMSize = $args['VRAMSize']; 1914 1915 // Video 1916 $m->accelerate3DEnabled = $args['accelerate3DEnabled']; 1917 $m->accelerate2DVideoEnabled = $args['accelerate2DVideoEnabled']; 1918 1919 // VRDE settings 1920 try { 1921 if($m->VRDEServer && $this->vbox->systemProperties->defaultVRDEExtPack) { 1922 $m->VRDEServer->enabled = $args['VRDEServer']['enabled']; 1923 $m->VRDEServer->setVRDEProperty('TCP/Ports',$args['VRDEServer']['ports']); 1924 if(@$this->settings->enableAdvancedConfig) 1925 $m->VRDEServer->setVRDEProperty('TCP/Address',$args['VRDEServer']['netAddress']); 1926 $m->VRDEServer->setVRDEProperty('VNCPassword',$args['VRDEServer']['VNCPassword'] ? $args['VRDEServer']['VNCPassword'] : null); 1927 $m->VRDEServer->authType = ($args['VRDEServer']['authType'] ? $args['VRDEServer']['authType'] : null); 1928 $m->VRDEServer->authTimeout = $args['VRDEServer']['authTimeout']; 1929 $m->VRDEServer->allowMultiConnection = $args['VRDEServer']['allowMultiConnection']; 1930 } 1931 } catch (Exception $e) { 1932 } 1933 1934 // Audio controller settings 1935 $m->audioAdapter->enabled = ($args['audioAdapter']['enabled'] ? 1 : 0); 1936 $m->audioAdapter->audioController = $args['audioAdapter']['audioController']; 1937 $m->audioAdapter->audioDriver = $args['audioAdapter']['audioDriver']; 1938 1939 // Boot order 1940 $mbp = $this->vbox->systemProperties->maxBootPosition; 1941 for($i = 0; $i < $mbp; $i ++) { 1942 if($args['bootOrder'][$i]) { 1943 $m->setBootOrder(($i + 1),$args['bootOrder'][$i]); 1944 } else { 1945 $m->setBootOrder(($i + 1),'Null'); 1946 } 1947 } 1948 1949 // Storage Controllers 1950 $scs = $m->storageControllers; 1951 $attachedEx = $attachedNew = array(); 1952 foreach($scs as $sc) { /* @var $sc IStorageController */ 1953 1954 $mas = $m->getMediumAttachmentsOfController($sc->name); 1955 1956 $cType = (string)$sc->controllerType; 1957 1958 foreach($mas as $ma) { /* @var $ma IMediumAttachment */ 1959 1960 $attachedEx[$sc->name.$ma->port.$ma->device] = (($ma->medium->handle && $ma->medium->id) ? $ma->medium->id : null); 1961 1962 // Remove IgnoreFlush key? 1963 if($this->settings->enableHDFlushConfig && (string)$ma->type == 'HardDisk') { 1964 $xtra = $this->_util_getIgnoreFlushKey($ma->port, $ma->device, $cType); 1965 if($xtra) { 1966 $m->setExtraData($xtra,''); 1967 } 1968 } 1969 1970 if($ma->controller) { 1971 $m->detachDevice($ma->controller,$ma->port,$ma->device); 1972 } 1973 1974 } 1975 $scname = $sc->name; 1976 $sc->releaseRemote(); 1977 $m->removeStorageController($scname); 1978 } 1979 1980 // Add New 1981 foreach($args['storageControllers'] as $sc) { 1982 1983 $sc['name'] = trim($sc['name']); 1984 $name = ($sc['name'] ? $sc['name'] : $sc['bus']); 1985 1986 1987 $bust = new StorageBus(null,$sc['bus']); 1988 $c = $m->addStorageController($name,(string)$bust); 1989 $c->controllerType = $sc['controllerType']; 1990 $c->useHostIOCache = $sc['useHostIOCache']; 1991 1992 // Set sata port count 1993 if($sc['bus'] == 'SATA') { 1994 $max = max(1,intval(@$sc['portCount'])); 1995 foreach($sc['mediumAttachments'] as $ma) { 1996 $max = max($max,(intval($ma['port'])+1)); 1997 } 1998 $c->portCount = min(intval($c->maxPortCount),max(count($sc['mediumAttachments']),$max)); 1999 2000 } 2001 $c->releaseRemote(); 2002 2003 2004 // Medium attachments 2005 foreach($sc['mediumAttachments'] as $ma) { 2006 2007 if($ma['medium'] == 'null') $ma['medium'] = null; 2008 2009 $attachedNew[$name.$ma['port'].$ma['device']] = $ma['medium']['id']; 2010 2011 if(is_array($ma['medium']) && $ma['medium']['id'] && $ma['type']) { 2012 2013 // Host drive 2014 if(strtolower($ma['medium']['hostDrive']) == 'true' || $ma['medium']['hostDrive'] === true) { 2015 // CD / DVD Drive 2016 if($ma['type'] == 'DVD') { 2017 $drives = $this->vbox->host->DVDDrives; 2018 // floppy drives 2019 } else { 2020 $drives = $this->vbox->host->floppyDrives; 2021 } 2022 foreach($drives as $md) { /* @var $md IMedium */ 2023 if($md->id == $ma['medium']['id']) { 2024 $med = &$md; 2025 break; 2026 } 2027 $md->releaseRemote(); 2028 } 2029 } else { 2030 /* @var $med IMedium */ 2031 $med = $this->vbox->openMedium($ma['medium']['location'],$ma['type'], 'ReadWrite', false); 2032 } 2033 } else { 2034 $med = null; 2035 } 2036 $m->attachDevice($name,$ma['port'],$ma['device'],$ma['type'],(is_object($med) ? $med->handle : null)); 2037 2038 // CD / DVD medium attachment type 2039 if($ma['type'] == 'DVD') { 2040 2041 if($ma['medium']['hostDrive']) 2042 $m->passthroughDevice($name, $ma['port'], $ma['device'], $ma['passthrough']); 2043 else 2044 $m->temporaryEjectDevice($name, $ma['port'], $ma['device'], $ma['temporaryEject']); 2045 2046 // HardDisk medium attachment type 2047 } else if($ma['type'] == 'HardDisk') { 2048 2049 $m->nonRotationalDevice($name, $ma['port'], $ma['device'], $ma['nonRotational']); 2050 2051 // Remove IgnoreFlush key? 2052 if($this->settings->enableHDFlushConfig) { 2053 2054 $xtra = $this->_util_getIgnoreFlushKey($ma['port'], $ma['device'], $sc['controllerType']); 2055 2056 if($xtra) { 2057 if($ma['ignoreFlush']) { 2058 $m->setExtraData($xtra, ''); 2059 } else { 2060 $m->setExtraData($xtra, 0); 2061 } 2062 } 2063 } 2064 2065 2066 } 2067 2068 if($sc['bus'] == 'SATA' || $sc['bus'] == 'USB') { 2069 $m->setHotPluggableForDevice($name, $ma['port'], $ma['device'], $ma['hotPluggable']); 2070 } 2071 2072 if(is_object($med)) 2073 $med->releaseRemote(); 2074 } 2075 2076 } 2077 2078 /* 2079 * 2080 * Network Adapters 2081 * 2082 */ 2083 2084 $netprops = array('enabled','attachmentType','adapterType','MACAddress','bridgedInterface', 2085 'hostOnlyInterface','internalNetwork','NATNetwork','cableConnected','promiscModePolicy','genericDriver'); 2086 2087 for($i = 0; $i < count($args['networkAdapters']); $i++) { 2088 if(@$this->settings->enableVDE) $netprops[] = 'VDENetwork'; 2089 2090 $n = $m->getNetworkAdapter($i); 2091 2092 // Skip disabled adapters 2093 if(!($n->enabled || @$args['networkAdapters'][$i]['enabled'])) 2094 continue; 2095 2096 for($p = 0; $p < count($netprops); $p++) { 2097 /* 2098 switch($netprops[$p]) { 2099 case 'enabled': 2100 case 'cableConnected': 2101 continue; 2102 } 2103 */ 2104 $n->{$netprops[$p]} = @$args['networkAdapters'][$i][$netprops[$p]]; 2105 } 2106 2107 // Special case for boolean values 2108 /* 2109 $n->enabled = $args['networkAdapters'][$i]['enabled']; 2110 $n->cableConnected = $args['networkAdapters'][$i]['cableConnected']; 2111 */ 2112 2113 // Network properties 2114 $eprops = $n->getProperties(null); 2115 $eprops = array_combine($eprops[1],$eprops[0]); 2116 $iprops = array_map(function($a){$b=explode("=",$a); return array($b[0]=>$b[1]);},preg_split('/[\r|\n]+/',$args['networkAdapters'][$i]['properties'])); 2117 $inprops = array(); 2118 foreach($iprops as $a) { 2119 foreach($a as $k=>$v) 2120 $inprops[$k] = $v; 2121 } 2122 // Remove any props that are in the existing properties array 2123 // but not in the incoming properties array 2124 foreach(array_diff(array_keys($eprops),array_keys($inprops)) as $dk) 2125 $n->setProperty($dk, ''); 2126 2127 // Set remaining properties 2128 foreach($inprops as $k => $v) 2129 $n->setProperty($k, $v); 2130 2131 // Nat redirects and advanced settings 2132 if($args['networkAdapters'][$i]['attachmentType'] == 'NAT') { 2133 2134 // Remove existing redirects 2135 foreach($n->NATEngine->getRedirects() as $r) { 2136 $n->NATEngine->removeRedirect(array_shift(explode(',',$r))); 2137 } 2138 // Add redirects 2139 foreach($args['networkAdapters'][$i]['redirects'] as $r) { 2140 $r = explode(',',$r); 2141 $n->NATEngine->addRedirect($r[0],$r[1],$r[2],$r[3],$r[4],$r[5]); 2142 } 2143 2144 // Advanced NAT settings 2145 if(@$this->settings->enableAdvancedConfig) { 2146 $aliasMode = $n->NATEngine->aliasMode & 1; 2147 if(intval($args['networkAdapters'][$i]['NATEngine']['aliasMode'] & 2)) $aliasMode |= 2; 2148 if(intval($args['networkAdapters'][$i]['NATEngine']['aliasMode'] & 4)) $aliasMode |= 4; 2149 $n->NATEngine->aliasMode = $aliasMode; 2150 $n->NATEngine->DNSProxy = $args['networkAdapters'][$i]['NATEngine']['DNSProxy']; 2151 $n->NATEngine->DNSPassDomain = $args['networkAdapters'][$i]['NATEngine']['DNSPassDomain']; 2152 $n->NATEngine->DNSUseHostResolver = $args['networkAdapters'][$i]['NATEngine']['DNSUseHostResolver']; 2153 $n->NATEngine->hostIP = $args['networkAdapters'][$i]['NATEngine']['hostIP']; 2154 } 2155 2156 } else if($args['networkAdapters'][$i]['attachmentType'] == 'NATNetwork') { 2157 2158 if($n->NATNetwork = $args['networkAdapters'][$i]['NATNetwork']); 2159 } 2160 2161 $n->releaseRemote(); 2162 } 2163 2164 // Serial Ports 2165 for($i = 0; $i < count($args['serialPorts']); $i++) { 2166 2167 /* @var $p ISerialPort */ 2168 $p = $m->getSerialPort($i); 2169 2170 if(!($p->enabled || $args['serialPorts'][$i]['enabled'])) 2171 continue; 2172 2173 try { 2174 $p->enabled = $args['serialPorts'][$i]['enabled']; 2175 $p->IOBase = @hexdec($args['serialPorts'][$i]['IOBase']); 2176 $p->IRQ = intval($args['serialPorts'][$i]['IRQ']); 2177 if($args['serialPorts'][$i]['path']) { 2178 $p->path = $args['serialPorts'][$i]['path']; 2179 $p->hostMode = $args['serialPorts'][$i]['hostMode']; 2180 } else { 2181 $p->hostMode = $args['serialPorts'][$i]['hostMode']; 2182 $p->path = $args['serialPorts'][$i]['path']; 2183 } 2184 $p->server = $args['serialPorts'][$i]['server']; 2185 $p->releaseRemote(); 2186 } catch (Exception $e) { 2187 $this->errors[] = $e; 2188 } 2189 } 2190 2191 // LPT Ports 2192 if(@$this->settings->enableLPTConfig) { 2193 $lptChanged = false; 2194 2195 for($i = 0; $i < count($args['parallelPorts']); $i++) { 2196 2197 /* @var $p IParallelPort */ 2198 $p = $m->getParallelPort($i); 2199 2200 if(!($p->enabled || $args['parallelPorts'][$i]['enabled'])) 2201 continue; 2202 2203 $lptChanged = true; 2204 try { 2205 $p->IOBase = @hexdec($args['parallelPorts'][$i]['IOBase']); 2206 $p->IRQ = intval($args['parallelPorts'][$i]['IRQ']); 2207 $p->path = $args['parallelPorts'][$i]['path']; 2208 $p->enabled = $args['parallelPorts'][$i]['enabled']; 2209 $p->releaseRemote(); 2210 } catch (Exception $e) { 2211 $this->errors[] = $e; 2212 } 2213 } 2214 } 2215 2216 2217 $sharedEx = array(); 2218 $sharedNew = array(); 2219 foreach($this->_machineGetSharedFolders($m) as $s) { 2220 $sharedEx[$s['name']] = array('name'=>$s['name'],'hostPath'=>$s['hostPath'],'autoMount'=>(bool)$s['autoMount'],'writable'=>(bool)$s['writable']); 2221 } 2222 foreach($args['sharedFolders'] as $s) { 2223 $sharedNew[$s['name']] = array('name'=>$s['name'],'hostPath'=>$s['hostPath'],'autoMount'=>(bool)$s['autoMount'],'writable'=>(bool)$s['writable']); 2224 } 2225 // Compare 2226 if(count($sharedEx) != count($sharedNew) || (@serialize($sharedEx) != @serialize($sharedNew))) { 2227 foreach($sharedEx as $s) { $m->removeSharedFolder($s['name']);} 2228 try { 2229 foreach($sharedNew as $s) { 2230 $m->createSharedFolder($s['name'],$s['hostPath'],(bool)$s['writable'],(bool)$s['autoMount']); 2231 } 2232 } catch (Exception $e) { $this->errors[] = $e; } 2233 } 2234 2235 // USB Filters 2236 2237 $usbEx = array(); 2238 $usbNew = array(); 2239 2240 $usbc = $this->_machineGetUSBControllers($this->session->machine); 2241 if(!$args['USBControllers'] || !is_array($args['USBControllers'])) $args['USBControllers'] = array(); 2242 2243 // Remove old 2244 $newNames = array(); 2245 $newByName = array(); 2246 foreach($args['USBControllers'] as $c) { 2247 $newNames[] = $c['name']; 2248 $newByName[$c['name']] = $c; 2249 } 2250 $exNames = array(); 2251 foreach($usbc as $c) { 2252 $exNames[] = $c['name']; 2253 if(in_array($c['name'], $newNames)) continue; 2254 $this->session->machine->removeUSBController($c['name']); 2255 } 2256 2257 $addNames = array_diff($newNames, $exNames); 2258 foreach($addNames as $name) { 2259 $this->session->machine->addUSBController($name, $newByName[$name]['type']); 2260 } 2261 2262 // filters 2263 $deviceFilters = $this->_machineGetUSBDeviceFilters($this->session->machine); 2264 if(!is_array($args['USBDeviceFilters'])) $args['USBDeviceFilters'] = array(); 2265 2266 if(count($deviceFilters) != count($args['USBDeviceFilters']) || @serialize($deviceFilters) != @serialize($args['USBDeviceFilters'])) { 2267 2268 // usb filter properties to change 2269 $usbProps = array('vendorId','productId','revision','manufacturer','product','serialNumber','port','remote'); 2270 2271 // Remove and Add filters 2272 try { 2273 2274 2275 $max = max(count($deviceFilters),count($args['USBDeviceFilters'])); 2276 $offset = 0; 2277 2278 // Remove existing 2279 for($i = 0; $i < $max; $i++) { 2280 2281 // Only if filter differs 2282 if(@serialize($deviceFilters[$i]) != @serialize($args['USBDeviceFilters'][$i])) { 2283 2284 // Remove existing? 2285 if($i < count($deviceFilters)) { 2286 $m->USBDeviceFilters->removeDeviceFilter(($i-$offset)); 2287 $offset++; 2288 } 2289 2290 // Exists in new? 2291 if(count($args['USBDeviceFilters'][$i])) { 2292 2293 // Create filter 2294 $f = $m->USBDeviceFilters->createDeviceFilter($args['USBDeviceFilters'][$i]['name']); 2295 $f->active = (bool)$args['USBDeviceFilters'][$i]['active']; 2296 2297 foreach($usbProps as $p) { 2298 $f->$p = $args['USBDeviceFilters'][$i][$p]; 2299 } 2300 2301 $m->USBDeviceFilters->insertDeviceFilter($i,$f->handle); 2302 $f->releaseRemote(); 2303 $offset--; 2304 } 2305 } 2306 2307 } 2308 2309 } catch (Exception $e) { $this->errors[] = $e; } 2310 2311 } 2312 2313 // Rename goes last 2314 if($m->name != $args['name']) { 2315 $m->name = $args['name']; 2316 } 2317 $this->session->machine->saveSettings(); 2318 2319 2320 $this->session->unlockMachine(); 2321 unset($this->session); 2322 $machine->releaseRemote(); 2323 2324 return true; 2325 2326 } 2327 2328 /** 2329 * Add a virtual machine via its settings file. 2330 * 2331 * @param array $args array of arguments. See function body for details. 2332 * @return boolean true on success 2333 */ 2334 public function remote_machineAdd($args) { 2335 2336 $this->connect(); 2337 2338 2339 /* @var $m IMachine */ 2340 $m = $this->vbox->openMachine($args['file']); 2341 $this->vbox->registerMachine($m->handle); 2342 2343 $m->releaseRemote(); 2344 2345 return true; 2346 2347 } 2348 2349 /** 2350 * Get progress operation status. On completion, destory progress operation. 2351 * 2352 * @param array $args array of arguments. See function body for details. 2353 * @return array response data 2354 */ 2355 public function remote_progressGet($args) { 2356 2357 // progress operation result 2358 $response = array(); 2359 $error = 0; 2360 2361 // Connect to vboxwebsrv 2362 $this->connect(); 2363 2364 try { 2365 2366 try { 2367 2368 // Force web call to keep session open. 2369 if($this->persistentRequest['sessionHandle']) { 2370 $this->session = new ISession($this->client, $this->persistentRequest['sessionHandle']); 2371 if((string)$this->session->state) {} 2372 } 2373 2374 /* @var $progress IProgress */ 2375 $progress = new IProgress($this->client, $args['progress']); 2376 2377 } catch (Exception $e) { 2378 $this->errors[] = $e; 2379 throw new Exception('Could not obtain progress operation: '.$args['progress']); 2380 } 2381 2382 $response['progress'] = $args['progress']; 2383 2384 $response['info'] = array( 2385 'completed' => $progress->completed, 2386 'canceled' => $progress->canceled, 2387 'description' => $progress->description, 2388 'operationDescription' => $progress->operationDescription, 2389 'timeRemaining' => $this->_util_splitTime($progress->timeRemaining), 2390 'timeElapsed' => $this->_util_splitTime((time() - $pop['started'])), 2391 'percent' => $progress->percent 2392 ); 2393 2394 2395 // Completed? Do not return. Fall to _util_progressDestroy() called later 2396 if($response['info']['completed'] || $response['info']['canceled']) { 2397 2398 try { 2399 if(!$response['info']['canceled'] && $progress->errorInfo->handle) { 2400 $error = array('message'=>$progress->errorInfo->text,'err'=>$this->_util_resultCodeText($progress->resultCode)); 2401 } 2402 } catch (Exception $null) {} 2403 2404 2405 } else { 2406 2407 $response['info']['cancelable'] = $progress->cancelable; 2408 2409 return $response; 2410 } 2411 2412 2413 } catch (Exception $e) { 2414 2415 // Force progress dialog closure 2416 $response['info'] = array('completed'=>1); 2417 2418 // Does an exception exist? 2419 try { 2420 if($progress->errorInfo->handle) { 2421 $error = array('message'=>$progress->errorInfo->text,'err'=>$this->_util_resultCodeText($progress->resultCode)); 2422 } 2423 } catch (Exception $null) {} 2424 2425 } 2426 2427 if($error) { 2428 if(@$args['catcherrs']) $response['error'] = $error; 2429 else $this->errors[] = new Exception($error['message']); 2430 2431 } 2432 2433 $this->_util_progressDestroy($pop); 2434 2435 return $response; 2436 2437 } 2438 2439 /** 2440 * Cancel a running progress operation 2441 * 2442 * @param array $args array of arguments. See function body for details. 2443 * @param array $response response data passed byref populated by the function 2444 * @return boolean true on success 2445 */ 2446 public function remote_progressCancel($args) { 2447 2448 // Connect to vboxwebsrv 2449 $this->connect(); 2450 2451 try { 2452 /* @var $progress IProgress */ 2453 $progress = new IProgress($this->client,$args['progress']); 2454 if(!($progress->completed || $progress->canceled)) 2455 $progress->cancel(); 2456 } catch (Exception $e) { 2457 $this->errors[] = $e; 2458 } 2459 2460 return true; 2461 } 2462 2463 /** 2464 * Destory a progress operation. 2465 * 2466 * @param array $pop progress operation details 2467 * @return boolean true on success 2468 */ 2469 private function _util_progressDestroy($pop) { 2470 2471 // Connect to vboxwebsrv 2472 $this->connect(); 2473 2474 try { 2475 /* @var $progress IProgress */ 2476 $progress = new IProgress($this->client,$pop['progress']); 2477 $progress->releaseRemote(); 2478 } catch (Exception $e) {} 2479 try { 2480 2481 // Close session and logoff 2482 try { 2483 2484 if($this->session->handle) { 2485 if((string)$this->session->state != 'Unlocked') { 2486 $this->session->unlockMachine(); 2487 } 2488 $this->session->releaseRemote(); 2489 unset($this->session); 2490 } 2491 2492 2493 } catch (Exception $e) { 2494 $this->errors[] = $e; 2495 } 2496 2497 2498 // Logoff session associated with progress operation 2499 $this->websessionManager->logoff($this->vbox->handle); 2500 unset($this->vbox); 2501 2502 } catch (Exception $e) { 2503 $this->errors[] = $e; 2504 } 2505 2506 // Remove progress handles 2507 $this->persistentRequest = array(); 2508 2509 return true; 2510 } 2511 2512 /** 2513 * Returns a key => value mapping of an enumeration class contained 2514 * in vboxServiceWrappers.php (classes that extend VBox_Enum). 2515 * 2516 * @param array $args array of arguments. See function body for details. 2517 * @return array response data 2518 * @see vboxServiceWrappers.php 2519 */ 2520 public function remote_vboxGetEnumerationMap($args) { 2521 2522 $c = new $args['class'](null, null); 2523 return (@isset($args['ValueMap']) ? $c->ValueMap : $c->NameMap); 2524 } 2525 2526 /** 2527 * Save VirtualBox system properties 2528 * 2529 * @param array $args array of arguments. See function body for details. 2530 * @return boolean true on success 2531 */ 2532 public function remote_vboxSystemPropertiesSave($args) { 2533 2534 // Connect to vboxwebsrv 2535 $this->connect(); 2536 2537 $this->vbox->systemProperties->defaultMachineFolder = $args['SystemProperties']['defaultMachineFolder']; 2538 $this->vbox->systemProperties->VRDEAuthLibrary = $args['SystemProperties']['VRDEAuthLibrary']; 2539 if(@$this->settings->vboxAutostartConfig) { 2540 $this->vbox->systemProperties->autostartDatabasePath = $args['SystemProperties']['autostartDatabasePath']; 2541 } 2542 2543 return true; 2544 2545 } 2546 2547 /** 2548 * Import a virtual appliance 2549 * 2550 * @param array $args array of arguments. See function body for details. 2551 * @return array response data 2552 */ 2553 public function remote_applianceImport($args) { 2554 2555 // Connect to vboxwebsrv 2556 $this->connect(); 2557 2558 /* @var $app IAppliance */ 2559 $app = $this->vbox->createAppliance(); 2560 2561 /* @var $progress IProgress */ 2562 $progress = $app->read($args['file']); 2563 2564 // Does an exception exist? 2565 try { 2566 if($progress->errorInfo->handle) { 2567 $this->errors[] = new Exception($progress->errorInfo->text); 2568 $app->releaseRemote(); 2569 return false; 2570 } 2571 } catch (Exception $null) {} 2572 2573 $progress->waitForCompletion(-1); 2574 2575 $app->interpret(); 2576 2577 $a = 0; 2578 foreach($app->virtualSystemDescriptions as $d) { /* @var $d IVirtualSystemDescription */ 2579 // Replace with passed values 2580 $args['descriptions'][$a][5] = array_pad($args['descriptions'][$a][5], count($args['descriptions'][$a][3]),true); 2581 foreach(array_keys($args['descriptions'][$a][5]) as $k) $args['descriptions'][$a][5][$k] = (bool)$args['descriptions'][$a][5][$k]; 2582 $d->setFinalValues($args['descriptions'][$a][5],$args['descriptions'][$a][3],$args['descriptions'][$a][4]); 2583 $a++; 2584 } 2585 2586 /* @var $progress IProgress */ 2587 $progress = $app->importMachines(array($args['reinitNetwork'] ? 'KeepNATMACs' : 'KeepAllMACs')); 2588 2589 $app->releaseRemote(); 2590 2591 // Does an exception exist? 2592 try { 2593 if($progress->errorInfo->handle) { 2594 $this->errors[] = new Exception($progress->errorInfo->text); 2595 $progress->releaseRemote(); 2596 return false; 2597 } 2598 } catch (Exception $null) {} 2599 2600 // Save progress 2601 $this->_util_progressStore($progress); 2602 2603 return array('progress' => $progress->handle); 2604 2605 } 2606 2607 /** 2608 * Get a list of VMs that are available for export. 2609 * 2610 * @param array $args array of arguments. See function body for details. 2611 * @return array list of exportable machiens 2612 */ 2613 public function remote_vboxGetExportableMachines($args) { 2614 2615 // Connect to vboxwebsrv 2616 $this->connect(); 2617 2618 //Get a list of registered machines 2619 $machines = $this->vbox->machines; 2620 2621 $response = array(); 2622 2623 foreach ($machines as $machine) { /* @var $machine IMachine */ 2624 2625 if ( @$this->settings->enforceVMOwnership && ($owner = $machine->getExtraData("phpvb/sso/owner")) && $owner !== $_SESSION['user'] && !$_SESSION['admin'] ) 2626 { 2627 // skip this VM as it is not owned by the user we're logged in as 2628 continue; 2629 } 2630 2631 try { 2632 $response[] = array( 2633 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $machine->name) : $machine->name, 2634 'state' => (string)$machine->state, 2635 'OSTypeId' => $machine->getOSTypeId(), 2636 'id' => $machine->id, 2637 'description' => $machine->description 2638 ); 2639 $machine->releaseRemote(); 2640 2641 } catch (Exception $e) { 2642 // Ignore. Probably inaccessible machine. 2643 } 2644 } 2645 return $response; 2646 } 2647 2648 2649 /** 2650 * Read and interpret virtual appliance file 2651 * 2652 * @param array $args array of arguments. See function body for details. 2653 * @return array appliance file content descriptions 2654 */ 2655 public function remote_applianceReadInterpret($args) { 2656 2657 // Connect to vboxwebsrv 2658 $this->connect(); 2659 2660 /* @var $app IAppliance */ 2661 $app = $this->vbox->createAppliance(); 2662 2663 /* @var $progress IProgress */ 2664 $progress = $app->read($args['file']); 2665 2666 // Does an exception exist? 2667 try { 2668 if($progress->errorInfo->handle) { 2669 $this->errors[] = new Exception($progress->errorInfo->text); 2670 $app->releaseRemote(); 2671 return false; 2672 } 2673 } catch (Exception $null) {} 2674 2675 $progress->waitForCompletion(-1); 2676 2677 $app->interpret(); 2678 2679 $response = array('warnings' => $app->getWarnings(), 2680 'descriptions' => array()); 2681 2682 $i = 0; 2683 foreach($app->virtualSystemDescriptions as $d) { /* @var $d IVirtualSystemDescription */ 2684 $desc = array(); 2685 $response['descriptions'][$i] = $d->getDescription(); 2686 foreach($response['descriptions'][$i][0] as $ddesc) { 2687 $desc[] = (string)$ddesc; 2688 } 2689 $response['descriptions'][$i][0] = $desc; 2690 $i++; 2691 $d->releaseRemote(); 2692 } 2693 $app->releaseRemote(); 2694 $app=null; 2695 2696 return $response; 2697 2698 } 2699 2700 2701 /** 2702 * Export VMs to a virtual appliance file 2703 * 2704 * @param array $args array of arguments. See function body for details. 2705 * @return array response data 2706 */ 2707 public function remote_applianceExport($args) { 2708 2709 // Connect to vboxwebsrv 2710 $this->connect(); 2711 2712 /* @var $app IAppliance */ 2713 $app = $this->vbox->createAppliance(); 2714 2715 // Overwrite existing file? 2716 if($args['overwrite']) { 2717 2718 $dsep = $this->getDsep(); 2719 2720 $path = str_replace($dsep.$dsep,$dsep,$args['file']); 2721 $dir = dirname($path); 2722 $file = basename($path); 2723 2724 if(substr($dir,-1) != $dsep) $dir .= $dsep; 2725 2726 /* @var $vfs IVFSExplorer */ 2727 $vfs = $app->createVFSExplorer('file://'.$dir); 2728 2729 /* @var $progress IProgress */ 2730 $progress = $vfs->remove(array($file)); 2731 $progress->waitForCompletion(-1); 2732 $progress->releaseRemote(); 2733 2734 $vfs->releaseRemote(); 2735 } 2736 2737 $appProps = array( 2738 'name' => 'Name', 2739 'description' => 'Description', 2740 'product' => 'Product', 2741 'vendor' => 'Vendor', 2742 'version' => 'Version', 2743 'product-url' => 'ProductUrl', 2744 'vendor-url' => 'VendorUrl', 2745 'license' => 'License'); 2746 2747 2748 foreach($args['vms'] as $vm) { 2749 2750 /* @var $m IMachine */ 2751 $m = $this->vbox->findMachine($vm['id']); 2752 if (@$this->settings->enforceVMOwnership && ($owner = $m->getExtraData("phpvb/sso/owner")) && $owner !== $_SESSION['user'] && !$_SESSION['admin'] ) 2753 { 2754 // skip this VM as it is not owned by the user we're logged in as 2755 continue; 2756 } 2757 $desc = $m->exportTo($app->handle, $args['file']); 2758 $props = $desc->getDescription(); 2759 $ptypes = array(); 2760 foreach($props[0] as $p) {$ptypes[] = (string)$p;} 2761 $typecount = 0; 2762 foreach($appProps as $k=>$v) { 2763 // Check for existing property 2764 if(($i=array_search($v,$ptypes)) !== false) { 2765 $props[3][$i] = $vm[$k]; 2766 } else { 2767 $desc->addDescription($v,$vm[$k],null); 2768 $props[3][] = $vm[$k]; 2769 $props[4][] = null; 2770 } 2771 $typecount++; 2772 } 2773 $enabled = array_pad(array(),count($props[3]),true); 2774 foreach(array_keys($enabled) as $k) $enabled[$k] = (bool)$enabled[$k]; 2775 $desc->setFinalValues($enabled,$props[3],$props[4]); 2776 $desc->releaseRemote(); 2777 $m->releaseRemote(); 2778 } 2779 2780 /* @var $progress IProgress */ 2781 $progress = $app->write($args['format'],($args['manifest'] ? array('CreateManifest') : array()),$args['file']); 2782 $app->releaseRemote(); 2783 2784 // Does an exception exist? 2785 try { 2786 if($progress->errorInfo->handle) { 2787 $this->errors[] = new Exception($progress->errorInfo->text); 2788 $progress->releaseRemote(); 2789 return false; 2790 } 2791 } catch (Exception $null) {} 2792 2793 // Save progress 2794 $this->_util_progressStore($progress); 2795 2796 return array('progress' => $progress->handle); 2797 2798 } 2799 2800 /** 2801 * Get nat network info 2802 * 2803 * @param unused $args 2804 * @param array $response response data passed byref populated by the function 2805 * @return array networking info data 2806 */ 2807 public function remote_vboxNATNetworksGet($args) { 2808 2809 $this->connect(); 2810 2811 $props = array('networkName','enabled','network','IPv6Enabled', 2812 'advertiseDefaultIPv6RouteEnabled','needDhcpServer','portForwardRules4', 2813 'portForwardRules6'); 2814 2815 $natNetworks = array(); 2816 2817 foreach($this->vbox->NATNetworks as $n) { 2818 2819 2820 $netDetails = array(); 2821 foreach($props as $p) { 2822 $netDetails[$p] = $n->$p; 2823 } 2824 2825 $natNetworks[] = $netDetails; 2826 } 2827 2828 return $natNetworks; 2829 2830 } 2831 2832 /** 2833 * Get nat network details 2834 * 2835 * @param array $args contains network name 2836 * @param array $response response data passed byref populated by the function 2837 * @return array networking info data 2838 */ 2839 public function remote_vboxNATNetworksSave($args) { 2840 2841 $this->connect(); 2842 2843 $props = array('networkName','enabled','network','IPv6Enabled', 2844 'advertiseDefaultIPv6RouteEnabled','needDhcpServer'); 2845 2846 $exNetworks = array(); 2847 foreach($this->vbox->NATNetworks as $n) { $exNetworks[$n->networkName] = false; } 2848 2849 /* Incoming network list */ 2850 foreach($args['networks'] as $net) { 2851 2852 /* Existing network */ 2853 if($net['orig_networkName']) { 2854 2855 $network = $this->vbox->findNATNetworkByName($net['orig_networkName']); 2856 2857 2858 $exNetworks[$net['orig_networkName']] = true; 2859 2860 foreach($props as $p) { 2861 $network->$p = $net[$p]; 2862 } 2863 2864 foreach(array('portForwardRules4','portForwardRules6') as $rules) { 2865 2866 if(!$net[$rules] || !is_array($net[$rules])) $net[$rules] = array(); 2867 2868 $rules_remove = array_diff($network->$rules, $net[$rules]); 2869 $rules_add = array_diff($net[$rules], $network->$rules); 2870 2871 foreach($rules_remove as $rule) { 2872 $network->removePortForwardRule((strpos($rules,'6')>-1), array_shift(preg_split('/:/',$rule))); 2873 } 2874 foreach($rules_add as $r) { 2875 preg_match('/(.*?):(.+?):\[(.*?)\]:(\d+):\[(.*?)\]:(\d+)/', $r, $rule); 2876 array_shift($rule); 2877 $network->addPortForwardRule((strpos($rules,'6')>-1), $rule[0],strtoupper($rule[1]),$rule[2],$rule[3],$rule[4],$rule[5]); 2878 } 2879 } 2880 2881 /* New network */ 2882 } else { 2883 2884 $network = $this->vbox->createNATNetwork($net['networkName']); 2885 2886 foreach($props as $p) { 2887 if($p == 'network' && $net[$p] == '') continue; 2888 $network->$p = $net[$p]; 2889 } 2890 2891 foreach($net['portForwardRules4'] as $r) { 2892 preg_match('/(.*?):(.+?):\[(.*?)\]:(\d+):\[(.*?)\]:(\d+)/', $r, $rule); 2893 array_shift($rule); 2894 $network->addPortForwardRule(false, $rule[0],strtoupper($rule[1]),$rule[2],$rule[3],$rule[4],$rule[5]); 2895 } 2896 foreach($net['portForwardRules6'] as $r) { 2897 preg_match('/(.*?):(.+?):\[(.*?)\]:(\d+):\[(.*?)\]:(\d+)/', $r, $rule); 2898 array_shift($rule); 2899 $network->addPortForwardRule(true, $rule[0],strtoupper($rule[1]),$rule[2],$rule[3],$rule[4],$rule[5]); 2900 } 2901 2902 } 2903 2904 } 2905 2906 /* Remove networks not in list */ 2907 foreach($exNetworks as $n=>$v) { 2908 if($v) continue; 2909 $n = $this->vbox->findNATNetworkByName($n); 2910 $this->vbox->removeNATNetwork($n); 2911 } 2912 2913 return true; 2914 2915 } 2916 2917 /** 2918 * Get networking info 2919 * 2920 * @param unused $args 2921 * @param array $response response data passed byref populated by the function 2922 * @return array networking info data 2923 */ 2924 public function remote_getNetworking($args) { 2925 2926 // Connect to vboxwebsrv 2927 $this->connect(); 2928 2929 $response = array(); 2930 $networks = array(); 2931 $nics = array(); 2932 $genericDrivers = array(); 2933 $vdenetworks = array(); 2934 2935 /* Get host nics */ 2936 foreach($this->vbox->host->networkInterfaces as $d) { /* @var $d IHostNetworkInterface */ 2937 $nics[] = $d->name; 2938 $d->releaseRemote(); 2939 } 2940 2941 /* Get internal Networks */ 2942 $networks = $this->vbox->internalNetworks; 2943 /* Generic Drivers */ 2944 $genericDrivers = $this->vbox->genericNetworkDrivers; 2945 2946 $natNetworks = array(); 2947 foreach($this->vbox->NATNetworks as $n) { 2948 $natNetworks[] = $n->networkName; 2949 } 2950 2951 return array( 2952 'nics' => $nics, 2953 'networks' => $networks, 2954 'genericDrivers' => $genericDrivers, 2955 'vdenetworks' => $vdenetworks, 2956 'natNetworks' => $natNetworks 2957 ); 2958 2959 } 2960 2961 /** 2962 * Get host-only interface information 2963 * 2964 * @param unused $args 2965 * @return array host only interface data 2966 */ 2967 public function remote_hostOnlyInterfacesGet($args) { 2968 2969 // Connect to vboxwebsrv 2970 $this->connect(); 2971 2972 /* 2973 * NICs 2974 */ 2975 $response = array('networkInterfaces' => array()); 2976 foreach($this->vbox->host->networkInterfaces as $d) { /* @var $d IHostNetworkInterface */ 2977 2978 if((string)$d->interfaceType != 'HostOnly') { 2979 $d->releaseRemote(); 2980 continue; 2981 } 2982 2983 2984 // Get DHCP Info 2985 try { 2986 /* @var $dhcp IDHCPServer */ 2987 $dhcp = $this->vbox->findDHCPServerByNetworkName($d->networkName); 2988 if($dhcp->handle) { 2989 $dhcpserver = array( 2990 'enabled' => $dhcp->enabled, 2991 'IPAddress' => $dhcp->IPAddress, 2992 'networkMask' => $dhcp->networkMask, 2993 'networkName' => $dhcp->networkName, 2994 'lowerIP' => $dhcp->lowerIP, 2995 'upperIP' => $dhcp->upperIP 2996 ); 2997 $dhcp->releaseRemote(); 2998 } else { 2999 $dhcpserver = array(); 3000 } 3001 } catch (Exception $e) { 3002 $dhcpserver = array(); 3003 } 3004 3005 $response['networkInterfaces'][] = array( 3006 'id' => $d->id, 3007 'IPV6Supported' => $d->IPV6Supported, 3008 'name' => $d->name, 3009 'IPAddress' => $d->IPAddress, 3010 'networkMask' => $d->networkMask, 3011 'IPV6Address' => $d->IPV6Address, 3012 'IPV6NetworkMaskPrefixLength' => $d->IPV6NetworkMaskPrefixLength, 3013 'DHCPEnabled' => $d->DHCPEnabled, 3014 'networkName' => $d->networkName, 3015 'dhcpServer' => $dhcpserver 3016 ); 3017 $d->releaseRemote(); 3018 } 3019 3020 return $response; 3021 } 3022 3023 3024 /** 3025 * Save host-only interface information 3026 * 3027 * @param array $args array of arguments. See function body for details. 3028 * @return boolean true on success 3029 */ 3030 public function remote_hostOnlyInterfacesSave($args) { 3031 3032 // Connect to vboxwebsrv 3033 $this->connect(); 3034 3035 $nics = $args['networkInterfaces']; 3036 3037 for($i = 0; $i < count($nics); $i++) { 3038 3039 /* @var $nic IHostNetworkInterface */ 3040 $nic = $this->vbox->host->findHostNetworkInterfaceById($nics[$i]['id']); 3041 3042 // Common settings 3043 if($nic->IPAddress != $nics[$i]['IPAddress'] || $nic->networkMask != $nics[$i]['networkMask']) { 3044 $nic->enableStaticIPConfig($nics[$i]['IPAddress'],$nics[$i]['networkMask']); 3045 } 3046 if($nics[$i]['IPV6Supported'] && 3047 ($nic->IPV6Address != $nics[$i]['IPV6Address'] || $nic->IPV6NetworkMaskPrefixLength != $nics[$i]['IPV6NetworkMaskPrefixLength'])) { 3048 $nic->enableStaticIPConfigV6($nics[$i]['IPV6Address'],intval($nics[$i]['IPV6NetworkMaskPrefixLength'])); 3049 } 3050 3051 // Get DHCP Info 3052 try { 3053 $dhcp = $this->vbox->findDHCPServerByNetworkName($nic->networkName); 3054 } catch (Exception $e) {$dhcp = null;}; 3055 3056 // Create DHCP server? 3057 if((bool)@$nics[$i]['dhcpServer']['enabled'] && !$dhcp) { 3058 $dhcp = $this->vbox->createDHCPServer($nic->networkName); 3059 } 3060 if($dhcp->handle) { 3061 $dhcp->enabled = @$nics[$i]['dhcpServer']['enabled']; 3062 $dhcp->setConfiguration($nics[$i]['dhcpServer']['IPAddress'],$nics[$i]['dhcpServer']['networkMask'],$nics[$i]['dhcpServer']['lowerIP'],$nics[$i]['dhcpServer']['upperIP']); 3063 $dhcp->releaseRemote(); 3064 } 3065 $nic->releaseRemote(); 3066 3067 } 3068 3069 return true; 3070 3071 } 3072 3073 /** 3074 * Add Host-only interface 3075 * 3076 * @param array $args array of arguments. See function body for details. 3077 * @return array response data 3078 */ 3079 public function remote_hostOnlyInterfaceCreate($args) { 3080 3081 // Connect to vboxwebsrv 3082 $this->connect(); 3083 3084 /* @var $progress IProgress */ 3085 list($int,$progress) = $this->vbox->host->createHostOnlyNetworkInterface(); 3086 $int->releaseRemote(); 3087 3088 // Does an exception exist? 3089 try { 3090 if($progress->errorInfo->handle) { 3091 $this->errors[] = new Exception($progress->errorInfo->text); 3092 $progress->releaseRemote(); 3093 return false; 3094 } 3095 } catch (Exception $null) {} 3096 3097 // Save progress 3098 $this->_util_progressStore($progress); 3099 3100 return array('progress' => $progress->handle); 3101 3102 3103 } 3104 3105 3106 /** 3107 * Remove a host-only interface 3108 * 3109 * @param array $args array of arguments. See function body for details. 3110 * @return array response data 3111 */ 3112 public function remote_hostOnlyInterfaceRemove($args) { 3113 3114 // Connect to vboxwebsrv 3115 $this->connect(); 3116 3117 /* @var $progress IProgress */ 3118 $progress = $this->vbox->host->removeHostOnlyNetworkInterface($args['id']); 3119 3120 if(!$progress->handle) return false; 3121 3122 // Does an exception exist? 3123 try { 3124 if($progress->errorInfo->handle) { 3125 $this->errors[] = new Exception($progress->errorInfo->text); 3126 $progress->releaseRemote(); 3127 return false; 3128 } 3129 } catch (Exception $null) {} 3130 3131 // Save progress 3132 $this->_util_progressStore($progress); 3133 3134 return array('progress' => $progress->handle); 3135 3136 } 3137 3138 /** 3139 * Get a list of Guest OS Types supported by this VirtualBox installation 3140 * 3141 * @param unused $args 3142 * @return array of os types 3143 */ 3144 public function remote_vboxGetGuestOSTypes($args) { 3145 3146 // Connect to vboxwebsrv 3147 $this->connect(); 3148 3149 $response = array(); 3150 3151 $ts = $this->vbox->getGuestOSTypes(); 3152 3153 $supp64 = ($this->vbox->host->getProcessorFeature('LongMode') && $this->vbox->host->getProcessorFeature('HWVirtEx')); 3154 3155 foreach($ts as $g) { /* @var $g IGuestOSType */ 3156 3157 // Avoid multiple calls 3158 $bit64 = $g->is64Bit; 3159 $response[] = array( 3160 'familyId' => $g->familyId, 3161 'familyDescription' => $g->familyDescription, 3162 'id' => $g->id, 3163 'description' => $g->description, 3164 'is64Bit' => $bit64, 3165 'recommendedRAM' => $g->recommendedRAM, 3166 'recommendedHDD' => ($g->recommendedHDD/1024)/1024, 3167 'supported' => (bool)(!$bit64 || $supp64) 3168 ); 3169 } 3170 3171 return $response; 3172 } 3173 3174 /** 3175 * Set virtual machine state. Running, power off, save state, pause, etc.. 3176 * 3177 * @param array $args array of arguments. See function body for details. 3178 * @return array response data or boolean true on success 3179 */ 3180 public function remote_machineSetState($args) { 3181 3182 $vm = $args['vm']; 3183 $state = $args['state']; 3184 3185 $states = array( 3186 'powerDown' => array('result'=>'PoweredOff','progress'=>2), 3187 'reset' => array(), 3188 'saveState' => array('result'=>'Saved','progress'=>2), 3189 'powerButton' => array('acpi'=>true), 3190 'sleepButton' => array('acpi'=>true), 3191 'pause' => array('result'=>'Paused','progress'=>false), 3192 'resume' => array('result'=>'Running','progress'=>false), 3193 'powerUp' => array('result'=>'Running'), 3194 'discardSavedState' => array('result'=>'poweredOff','lock'=>'shared','force'=>true) 3195 ); 3196 3197 // Check for valid state 3198 if(!is_array($states[$state])) { 3199 throw new Exception('Invalid state: ' . $state); 3200 } 3201 3202 // Connect to vboxwebsrv 3203 $this->connect(); 3204 3205 // Machine state 3206 /* @var $machine IMachine */ 3207 $machine = $this->vbox->findMachine($vm); 3208 $mstate = (string)$machine->state; 3209 3210 if (@$this->settings->enforceVMOwnership && !$this->skipSessionCheck && ($owner = $machine->getExtraData("phpvb/sso/owner")) && $owner !== $_SESSION['user'] && !$_SESSION['admin'] ) 3211 { 3212 // skip this VM as it is not owned by the user we're logged in as 3213 throw new Exception("Not authorized to change state of this VM"); 3214 } 3215 3216 // If state has an expected result, check 3217 // that we are not already in it 3218 if($states[$state]['result']) { 3219 if($mstate == $states[$state]['result']) { 3220 $machine->releaseRemote(); 3221 return false; 3222 } 3223 } 3224 3225 // Special case for power up 3226 if($state == 'powerUp' && $mstate == 'Paused') 3227 $state = 'resume'; 3228 3229 if($state == 'powerUp') { 3230 3231 3232 # Try opening session for VM 3233 try { 3234 3235 // create session 3236 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 3237 3238 // set first run 3239 if($machine->getExtraData('GUI/FirstRun') == 'yes') { 3240 $machine->lockMachine($this->session->handle, 'Write'); 3241 $this->session->machine->setExtraData('GUI/FirstRun', 'no'); 3242 $this->session->unlockMachine(); 3243 } 3244 3245 /* @var $progress IProgress */ 3246 $progress = $machine->launchVMProcess($this->session->handle, "headless", ""); 3247 3248 } catch (Exception $e) { 3249 // Error opening session 3250 $this->errors[] = $e; 3251 return false; 3252 } 3253 3254 // Does an exception exist? 3255 try { 3256 if($progress->errorInfo->handle) { 3257 $this->errors[] = new Exception($progress->errorInfo->text); 3258 $progress->releaseRemote(); 3259 return false; 3260 } 3261 } catch (Exception $null) { 3262 } 3263 3264 $this->_util_progressStore($progress); 3265 3266 return array('progress' => $progress->handle); 3267 3268 3269 } 3270 3271 // Open session to machine 3272 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 3273 3274 // Lock machine 3275 $machine->lockMachine($this->session->handle,($states[$state]['lock'] == 'write' ? 'Write' : 'Shared')); 3276 3277 // If this operation returns a progress object save progress 3278 $progress = null; 3279 if($states[$state]['progress']) { 3280 3281 /* @var $progress IProgress */ 3282 if($state == 'saveState') { 3283 $progress = $this->session->machine->saveState(); 3284 } else { 3285 $progress = $this->session->console->$state(); 3286 } 3287 3288 if(!$progress->handle) { 3289 3290 // should never get here 3291 try { 3292 $this->session->unlockMachine(); 3293 $this->session = null; 3294 } catch (Exception $e) {}; 3295 3296 $machine->releaseRemote(); 3297 3298 throw new Exception('Unknown error settings machine to requested state.'); 3299 } 3300 3301 // Does an exception exist? 3302 try { 3303 if($progress->errorInfo->handle) { 3304 $this->errors[] = new Exception($progress->errorInfo->text); 3305 $progress->releaseRemote(); 3306 return false; 3307 } 3308 } catch (Exception $null) {} 3309 3310 // Save progress 3311 $this->_util_progressStore($progress); 3312 3313 return array('progress' => $progress->handle); 3314 3315 // Operation does not return a progress object 3316 // Just call the function 3317 } else { 3318 3319 if($state == 'discardSavedState') { 3320 $this->session->machine->$state(($states[$state]['force'] ? true : null)); 3321 } else { 3322 $this->session->console->$state(($states[$state]['force'] ? true : null)); 3323 } 3324 3325 } 3326 3327 $vmname = $machine->name; 3328 $machine->releaseRemote(); 3329 3330 // Check for ACPI button 3331 if($states[$state]['acpi'] && !$this->session->console->getPowerButtonHandled()) { 3332 $this->session->console->releaseRemote(); 3333 $this->session->unlockMachine(); 3334 $this->session = null; 3335 return false; 3336 } 3337 3338 3339 if(!$progress->handle) { 3340 $this->session->console->releaseRemote(); 3341 $this->session->unlockMachine(); 3342 unset($this->session); 3343 } 3344 3345 return true; 3346 3347 } 3348 3349 3350 /** 3351 * Get VirtualBox host memory usage information 3352 * 3353 * @param unused $args 3354 * @return array response data 3355 */ 3356 public function remote_hostGetMeminfo($args) { 3357 3358 // Connect to vboxwebsrv 3359 $this->connect(); 3360 3361 return $this->vbox->host->memoryAvailable; 3362 3363 } 3364 3365 /** 3366 * Get VirtualBox host details 3367 * 3368 * @param unused $args 3369 * @return array response data 3370 */ 3371 public function remote_hostGetDetails($args) { 3372 3373 // Connect to vboxwebsrv 3374 $this->connect(); 3375 3376 /* @var $host IHost */ 3377 $host = &$this->vbox->host; 3378 $response = array( 3379 'id' => 'host', 3380 'operatingSystem' => $host->operatingSystem, 3381 'OSVersion' => $host->OSVersion, 3382 'memorySize' => $host->memorySize, 3383 'acceleration3DAvailable' => $host->acceleration3DAvailable, 3384 'cpus' => array(), 3385 'networkInterfaces' => array(), 3386 'DVDDrives' => array(), 3387 'floppyDrives' => array() 3388 ); 3389 3390 /* 3391 * Processors 3392 */ 3393 // TODO https://github.com/phpvirtualbox/phpvirtualbox/issues/53 3394 $response['cpus'][0] = $host->getProcessorDescription(0); 3395 for($i = 1; $i < $host->processorCount; $i++) { 3396 $response['cpus'][$i] = $response['cpus'][0]; 3397 } 3398 3399 /* 3400 * Supported CPU features? 3401 */ 3402 $response['cpuFeatures'] = array(); 3403 foreach(array('HWVirtEx'=>'HWVirtEx','PAE'=>'PAE','NestedPaging'=>'Nested Paging','LongMode'=>'Long Mode (64-bit)') as $k=>$v) { 3404 $response['cpuFeatures'][$v] = $host->getProcessorFeature($k); 3405 } 3406 3407 /* 3408 * NICs 3409 */ 3410 foreach($host->networkInterfaces as $d) { /* @var $d IHostNetworkInterface */ 3411 $response['networkInterfaces'][] = array( 3412 'name' => $d->name, 3413 'IPAddress' => $d->IPAddress, 3414 'networkMask' => $d->networkMask, 3415 'IPV6Supported' => $d->IPV6Supported, 3416 'IPV6Address' => $d->IPV6Address, 3417 'IPV6NetworkMaskPrefixLength' => $d->IPV6NetworkMaskPrefixLength, 3418 'status' => (string)$d->status, 3419 'mediumType' => (string)$d->mediumType, 3420 'interfaceType' => (string)$d->interfaceType, 3421 'hardwareAddress' => $d->hardwareAddress, 3422 'networkName' => $d->networkName, 3423 ); 3424 $d->releaseRemote(); 3425 } 3426 3427 /* 3428 * Medium types (DVD and Floppy) 3429 */ 3430 foreach($host->DVDDrives as $d) { /* @var $d IMedium */ 3431 3432 $response['DVDDrives'][] = array( 3433 'id' => $d->id, 3434 'name' => $d->name, 3435 'location' => $d->location, 3436 'description' => $d->description, 3437 'deviceType' => 'DVD', 3438 'hostDrive' => true 3439 ); 3440 $d->releaseRemote(); 3441 } 3442 3443 foreach($host->floppyDrives as $d) { /* @var $d IMedium */ 3444 3445 $response['floppyDrives'][] = array( 3446 'id' => $d->id, 3447 'name' => $d->name, 3448 'location' => $d->location, 3449 'description' => $d->description, 3450 'deviceType' => 'Floppy', 3451 'hostDrive' => true, 3452 ); 3453 $d->releaseRemote(); 3454 } 3455 $host->releaseRemote(); 3456 3457 return $response; 3458 } 3459 3460 /** 3461 * Get a list of USB devices attached to the VirtualBox host 3462 * 3463 * @param unused $args 3464 * @return array of USB devices 3465 */ 3466 public function remote_hostGetUSBDevices($args) { 3467 3468 // Connect to vboxwebsrv 3469 $this->connect(); 3470 3471 $response = array(); 3472 3473 foreach($this->vbox->host->USBDevices as $d) { /* @var $d IUSBDevice */ 3474 3475 $response[] = array( 3476 'id' => $d->id, 3477 'vendorId' => sprintf('%04s',dechex($d->vendorId)), 3478 'productId' => sprintf('%04s',dechex($d->productId)), 3479 'revision' => sprintf('%04s',dechex($d->revision)), 3480 'manufacturer' => $d->manufacturer, 3481 'product' => $d->product, 3482 'serialNumber' => $d->serialNumber, 3483 'address' => $d->address, 3484 'port' => $d->port, 3485 'version' => $d->version, 3486 'portVersion' => $d->portVersion, 3487 'remote' => $d->remote, 3488 'state' => (string)$d->state, 3489 ); 3490 $d->releaseRemote(); 3491 } 3492 3493 return $response; 3494 } 3495 3496 3497 /** 3498 * Get virtual machine or virtualbox host details 3499 * 3500 * @param array $args array of arguments. See function body for details. 3501 * @param ISnapshot $snapshot snapshot instance to use if obtaining snapshot details. 3502 * @see hostGetDetails() 3503 * @return array machine details 3504 */ 3505 public function remote_machineGetDetails($args, $snapshot=null) { 3506 3507 // Host instead of vm info 3508 if($args['vm'] == 'host') { 3509 3510 $response = $this->remote_hostGetDetails($args); 3511 3512 return $response; 3513 } 3514 3515 3516 // Connect to vboxwebsrv 3517 $this->connect(); 3518 3519 //Get registered machine or snapshot machine 3520 if($snapshot) { 3521 3522 /* @var $machine ISnapshot */ 3523 $machine = &$snapshot; 3524 3525 } else { 3526 3527 /* @var $machine IMachine */ 3528 $machine = $this->vbox->findMachine($args['vm']); 3529 3530 3531 // For correct caching, always use id even if a name was passed 3532 $args['vm'] = $machine->id; 3533 3534 // Check for accessibility 3535 if(!$machine->accessible) { 3536 3537 return array( 3538 'name' => $machine->id, 3539 'state' => 'Inaccessible', 3540 'OSTypeId' => 'Other', 3541 'id' => $machine->id, 3542 'sessionState' => 'Inaccessible', 3543 'accessible' => 0, 3544 'accessError' => array( 3545 'resultCode' => $this->_util_resultCodeText($machine->accessError->resultCode), 3546 'component' => $machine->accessError->component, 3547 'text' => $machine->accessError->text) 3548 ); 3549 } 3550 3551 } 3552 3553 // Basic data 3554 $data = $this->_machineGetDetails($machine); 3555 3556 // Network Adapters 3557 $data['networkAdapters'] = $this->_machineGetNetworkAdapters($machine); 3558 3559 // Storage Controllers 3560 $data['storageControllers'] = $this->_machineGetStorageControllers($machine); 3561 3562 // Serial Ports 3563 $data['serialPorts'] = $this->_machineGetSerialPorts($machine); 3564 3565 // LPT Ports 3566 $data['parallelPorts'] = $this->_machineGetParallelPorts($machine); 3567 3568 // Shared Folders 3569 $data['sharedFolders'] = $this->_machineGetSharedFolders($machine); 3570 3571 // USB Controllers 3572 $data['USBControllers'] = $this->_machineGetUSBControllers($machine); 3573 $data['USBDeviceFilters'] = $this->_machineGetUSBDeviceFilters($machine); 3574 3575 3576 if (@$this->settings->enforceVMOwnership ) 3577 { 3578 $data['name'] = preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $data['name']); 3579 } 3580 3581 // Items when not obtaining snapshot machine info 3582 if(!$snapshot) { 3583 3584 $data['currentSnapshot'] = ($machine->currentSnapshot->handle ? array('id'=>$machine->currentSnapshot->id,'name'=>$machine->currentSnapshot->name) : null); 3585 $data['snapshotCount'] = $machine->snapshotCount; 3586 3587 // Start / stop config 3588 if(@$this->settings->startStopConfig) { 3589 $data['startupMode'] = $machine->getExtraData('pvbx/startupMode'); 3590 } 3591 3592 3593 } 3594 3595 $machine->releaseRemote(); 3596 3597 $data['accessible'] = 1; 3598 return $data; 3599 } 3600 3601 /** 3602 * Get runtime data of machine. 3603 * 3604 * @param array $args array of arguments. See function body for details. 3605 * @return array of machine runtime data 3606 */ 3607 public function remote_machineGetRuntimeData($args) { 3608 3609 $this->connect(); 3610 3611 /* @var $machine IMachine */ 3612 $machine = $this->vbox->findMachine($args['vm']); 3613 $data = array( 3614 'id' => $args['vm'], 3615 'state' => (string)$machine->state 3616 ); 3617 3618 /* 3619 * TODO: 3620 * 3621 * 5.13.13 getGuestEnteredACPIMode 3622 boolean IConsole::getGuestEnteredACPIMode() 3623 Checks if the guest entered the ACPI mode G0 (working) or G1 (sleeping). If this method 3624 returns false, the guest will most likely not respond to external ACPI events. 3625 If this method fails, the following error codes may be reported: 3626 VBOX_E_INVALID_VM_STATE: Virtual machine not in Running state. 3627 */ 3628 3629 // Get current console port 3630 if($data['state'] == 'Running' || $data['state'] == 'Paused') { 3631 3632 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 3633 $machine->lockMachine($this->session->handle, 'Shared'); 3634 $console = $this->session->console; 3635 3636 // Get guest additions version 3637 if(@$this->settings->enableGuestAdditionsVersionDisplay) { 3638 $data['guestAdditionsVersion'] = $console->guest->additionsVersion; 3639 } 3640 3641 $smachine = $this->session->machine; 3642 3643 $data['CPUExecutionCap'] = $smachine->CPUExecutionCap; 3644 $data['VRDEServerInfo'] = array('port' => $console->VRDEServerInfo->port); 3645 3646 $vrde = $smachine->VRDEServer; 3647 3648 $data['VRDEServer'] = (!$vrde ? null : array( 3649 'enabled' => $vrde->enabled, 3650 'ports' => $vrde->getVRDEProperty('TCP/Ports'), 3651 'netAddress' => $vrde->getVRDEProperty('TCP/Address'), 3652 'VNCPassword' => $vrde->getVRDEProperty('VNCPassword'), 3653 'authType' => (string)$vrde->authType, 3654 'authTimeout' => $vrde->authTimeout, 3655 'VRDEExtPack' => (string)$vrde->VRDEExtPack 3656 )); 3657 3658 // Get removable media 3659 $data['storageControllers'] = $this->_machineGetStorageControllers($smachine); 3660 3661 // Get network adapters 3662 $data['networkAdapters'] = $this->_machineGetNetworkAdapters($smachine); 3663 3664 $machine->releaseRemote(); 3665 3666 // Close session and unlock machine 3667 $this->session->unlockMachine(); 3668 unset($this->session); 3669 3670 } 3671 3672 3673 return $data; 3674 3675 } 3676 3677 /** 3678 * Remove a virtual machine 3679 * 3680 * @param array $args array of arguments. See function body for details. 3681 * @return boolean true on success or array of response data 3682 */ 3683 public function remote_machineRemove($args) { 3684 3685 // Connect to vboxwebsrv 3686 $this->connect(); 3687 3688 /* @var $machine IMachine */ 3689 $machine = $this->vbox->findMachine($args['vm']); 3690 3691 // Only unregister or delete? 3692 if(!$args['delete']) { 3693 3694 $machine->unregister('DetachAllReturnNone'); 3695 $machine->releaseRemote(); 3696 3697 } else { 3698 3699 $hds = array(); 3700 $delete = $machine->unregister('DetachAllReturnHardDisksOnly'); 3701 foreach($delete as $hd) { 3702 $hds[] = $this->vbox->openMedium($hd->location,'HardDisk','ReadWrite',false)->handle; 3703 } 3704 3705 /* @var $progress IProgress */ 3706 $progress = $machine->deleteConfig($hds); 3707 3708 $machine->releaseRemote(); 3709 3710 // Does an exception exist? 3711 if($progress) { 3712 try { 3713 if($progress->errorInfo->handle) { 3714 $this->errors[] = new Exception($progress->errorInfo->text); 3715 $progress->releaseRemote(); 3716 return false; 3717 } 3718 } catch (Exception $null) {} 3719 3720 $this->_util_progressStore($progress); 3721 3722 return array('progress' => $progress->handle); 3723 3724 } 3725 3726 3727 } 3728 3729 return true; 3730 3731 3732 } 3733 3734 3735 /** 3736 * Create a new Virtual Machine 3737 * 3738 * @param array $args array of arguments. See function body for details. 3739 * @return boolean true on success 3740 */ 3741 public function remote_machineCreate($args) { 3742 3743 // Connect to vboxwebsrv 3744 $this->connect(); 3745 3746 $response = array(); 3747 3748 // quota enforcement 3749 if ( isset($_SESSION['user']) ) 3750 { 3751 if ( @isset($this->settings->vmQuotaPerUser) && @$this->settings->vmQuotaPerUser > 0 && !$_SESSION['admin'] ) 3752 { 3753 $newresp = array('data' => array()); 3754 $this->vboxGetMachines(array(), array(&$newresp)); 3755 if ( count($newresp['data']['responseData']) >= $this->settings->vmQuotaPerUser ) 3756 { 3757 // we're over quota! 3758 // delete the disk we just created 3759 if ( isset($args['disk']) ) 3760 { 3761 $this->mediumRemove(array( 3762 'medium' => $args['disk'], 3763 'type' => 'HardDisk', 3764 'delete' => true 3765 ), $newresp); 3766 } 3767 throw new Exception("Sorry, you're over quota. You can only create up to {$this->settings->vmQuotaPerUser} VMs."); 3768 } 3769 } 3770 } 3771 3772 // create machine 3773 if (@$this->settings->enforceVMOwnership ) 3774 $args['name'] = $_SESSION['user'] . '_' . $args['name']; 3775 3776 /* Check if file exists */ 3777 $filename = $this->vbox->composeMachineFilename($args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$this->vbox->systemProperties->defaultMachineFolder,null); 3778 3779 if($this->remote_fileExists(array('file'=>$filename))) { 3780 return array('exists' => $filename); 3781 } 3782 3783 3784 /* @var $m IMachine */ 3785 $m = $this->vbox->createMachine(null,$args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$args['ostype'],null,null); 3786 3787 /* Check for phpVirtualBox groups */ 3788 if($this->settings->phpVboxGroups && $args['group']) { 3789 $m->setExtraData(vboxconnector::phpVboxGroupKey, $args['group']); 3790 } 3791 3792 // Set memory 3793 $m->memorySize = intval($args['memory']); 3794 3795 3796 // Save and register 3797 $m->saveSettings(); 3798 $this->vbox->registerMachine($m->handle); 3799 $vm = $m->id; 3800 $m->releaseRemote(); 3801 3802 try { 3803 3804 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 3805 3806 // Lock VM 3807 /* @var $machine IMachine */ 3808 $machine = $this->vbox->findMachine($vm); 3809 $machine->lockMachine($this->session->handle,'Write'); 3810 3811 // OS defaults 3812 $defaults = $this->vbox->getGuestOSType($args['ostype']); 3813 3814 // Ownership enforcement 3815 if ( isset($_SESSION['user']) ) 3816 { 3817 $this->session->machine->setExtraData('phpvb/sso/owner', $_SESSION['user']); 3818 } 3819 3820 // set the vboxauthsimple in VM config 3821 $this->session->machine->setExtraData('VBoxAuthSimple/users/'.$_SESSION['user'].'', $_SESSION['uHash']); 3822 3823 // Always set 3824 $this->session->machine->setExtraData('GUI/FirstRun', 'yes'); 3825 3826 try { 3827 if($this->session->machine->VRDEServer && $this->vbox->systemProperties->defaultVRDEExtPack) { 3828 $this->session->machine->VRDEServer->enabled = 1; 3829 $this->session->machine->VRDEServer->authTimeout = 5000; 3830 $this->session->machine->VRDEServer->setVRDEProperty('TCP/Ports',($this->settings->vrdeports ? $this->settings->vrdeports : '3390-5000')); 3831 $this->session->machine->VRDEServer->setVRDEProperty('TCP/Address',($this->settings->vrdeaddress ? $this->settings->vrdeaddress : '127.0.0.1')); 3832 } 3833 } catch (Exception $e) { 3834 //Ignore 3835 } 3836 3837 // Other defaults 3838 $this->session->machine->BIOSSettings->IOAPICEnabled = $defaults->recommendedIOAPIC; 3839 $this->session->machine->RTCUseUTC = $defaults->recommendedRTCUseUTC; 3840 $this->session->machine->firmwareType = (string)$defaults->recommendedFirmware; 3841 $this->session->machine->chipsetType = (string)$defaults->recommendedChipset; 3842 if(intval($defaults->recommendedVRAM) > 0) $this->session->machine->VRAMSize = intval($defaults->recommendedVRAM); 3843 $this->session->machine->setCpuProperty('PAE',$defaults->recommendedPAE); 3844 3845 // USB input devices 3846 if($defaults->recommendedUSBHid) { 3847 $this->session->machine->pointingHIDType = 'USBMouse'; 3848 $this->session->machine->keyboardHIDType = 'USBKeyboard'; 3849 } 3850 3851 /* Only if acceleration configuration is available */ 3852 if($this->vbox->host->getProcessorFeature('HWVirtEx')) { 3853 $this->session->machine->setHWVirtExProperty('Enabled',$defaults->recommendedVirtEx); 3854 } 3855 3856 /* 3857 * Hard Disk and DVD/CD Drive 3858 */ 3859 $DVDbusType = (string)$defaults->recommendedDVDStorageBus; 3860 $DVDconType = (string)$defaults->recommendedDVDStorageController; 3861 3862 // Attach harddisk? 3863 if($args['disk']) { 3864 3865 $HDbusType = (string)$defaults->recommendedHDStorageBus; 3866 $HDconType = (string)$defaults->recommendedHDStorageController; 3867 3868 $bus = new StorageBus(null,$HDbusType); 3869 $sc = $this->session->machine->addStorageController(trans($HDbusType,'UIMachineSettingsStorage'),(string)$bus); 3870 $sc->controllerType = $HDconType; 3871 $sc->useHostIOCache = (bool)$this->vbox->systemProperties->getDefaultIoCacheSettingForStorageController($HDconType); 3872 3873 // Set port count? 3874 if($HDbusType == 'SATA') { 3875 $sc->portCount = (($HDbusType == $DVDbusType) ? 2 : 1); 3876 } 3877 3878 $sc->releaseRemote(); 3879 3880 $m = $this->vbox->openMedium($args['disk'],'HardDisk','ReadWrite',false); 3881 3882 $this->session->machine->attachDevice(trans($HDbusType,'UIMachineSettingsStorage'),0,0,'HardDisk',$m->handle); 3883 3884 $m->releaseRemote(); 3885 3886 } 3887 3888 // Attach DVD/CDROM 3889 if($DVDbusType) { 3890 3891 if(!$args['disk'] || ($HDbusType != $DVDbusType)) { 3892 3893 $bus = new StorageBus(null,$DVDbusType); 3894 $sc = $this->session->machine->addStorageController(trans($DVDbusType,'UIMachineSettingsStorage'),(string)$bus); 3895 $sc->controllerType = $DVDconType; 3896 $sc->useHostIOCache = (bool)$this->vbox->systemProperties->getDefaultIoCacheSettingForStorageController($DVDconType); 3897 3898 // Set port count? 3899 if($DVDbusType == 'SATA') { 3900 $sc->portCount = ($args['disk'] ? 1 : 2); 3901 } 3902 3903 $sc->releaseRemote(); 3904 } 3905 3906 $this->session->machine->attachDevice(trans($DVDbusType,'UIMachineSettingsStorage'),1,0,'DVD',null); 3907 3908 } 3909 3910 $this->session->machine->saveSettings(); 3911 $this->session->unlockMachine(); 3912 $this->session = null; 3913 3914 $machine->releaseRemote(); 3915 3916 } catch (Exception $e) { 3917 $this->errors[] = $e; 3918 return false; 3919 } 3920 3921 return true; 3922 3923 } 3924 3925 3926 /** 3927 * Return a list of network adapters attached to machine $m 3928 * 3929 * @param IMachine $m virtual machine instance 3930 * @param int $slot optional slot of single network adapter to get 3931 * @return array of network adapter information 3932 */ 3933 private function _machineGetNetworkAdapters(&$m, $slot=false) { 3934 3935 $adapters = array(); 3936 3937 for($i = ($slot === false ? 0 : $slot); $i < ($slot === false ? $this->settings->nicMax : ($slot+1)); $i++) { 3938 3939 /* @var $n INetworkAdapter */ 3940 $n = $m->getNetworkAdapter($i); 3941 3942 // Avoid duplicate calls 3943 $at = (string)$n->attachmentType; 3944 if($at == 'NAT') $nd = $n->NATEngine; /* @var $nd INATEngine */ 3945 else $nd = null; 3946 3947 $props = $n->getProperties(null); 3948 $props = implode("\n",array_map(function($a,$b){return "$a=$b";},$props[1],$props[0])); 3949 3950 $adapters[] = array( 3951 'adapterType' => (string)$n->adapterType, 3952 'slot' => $n->slot, 3953 'enabled' => $n->enabled, 3954 'MACAddress' => $n->MACAddress, 3955 'attachmentType' => $at, 3956 'genericDriver' => $n->genericDriver, 3957 'hostOnlyInterface' => $n->hostOnlyInterface, 3958 'bridgedInterface' => $n->bridgedInterface, 3959 'properties' => $props, 3960 'internalNetwork' => $n->internalNetwork, 3961 'NATNetwork' => $n->NATNetwork, 3962 'promiscModePolicy' => (string)$n->promiscModePolicy, 3963 'VDENetwork' => ($this->settings->enableVDE ? $n->VDENetwork : ''), 3964 'cableConnected' => $n->cableConnected, 3965 'NATEngine' => ($at == 'NAT' ? 3966 array('aliasMode' => intval($nd->aliasMode),'DNSPassDomain' => $nd->DNSPassDomain, 'DNSProxy' => $nd->DNSProxy, 'DNSUseHostResolver' => $nd->DNSUseHostResolver, 'hostIP' => $nd->hostIP) 3967 : array('aliasMode' => 0,'DNSPassDomain' => 0, 'DNSProxy' => 0, 'DNSUseHostResolver' => 0, 'hostIP' => '')), 3968 'lineSpeed' => $n->lineSpeed, 3969 'redirects' => ( 3970 $at == 'NAT' ? 3971 $nd->getRedirects() 3972 : array() 3973 ) 3974 ); 3975 3976 $n->releaseRemote(); 3977 } 3978 3979 return $adapters; 3980 3981 } 3982 3983 3984 /** 3985 * Return a list of virtual machines along with their states and other basic info 3986 * 3987 * @param array $args array of arguments. See function body for details. 3988 * @return array list of machines 3989 */ 3990 public function remote_vboxGetMachines($args) { 3991 3992 // Connect to vboxwebsrv 3993 $this->connect(); 3994 3995 $vmlist = array(); 3996 3997 // Look for a request for a single vm 3998 if($args['vm']) { 3999 4000 $machines = array($this->vbox->findMachine($args['vm'])); 4001 4002 // Full list 4003 } else { 4004 //Get a list of registered machines 4005 $machines = $this->vbox->machines; 4006 4007 } 4008 4009 4010 4011 foreach ($machines as $machine) { /* @var $machine IMachine */ 4012 4013 4014 try { 4015 4016 if(!$machine->accessible) { 4017 4018 $vmlist[] = array( 4019 'name' => $machine->id, 4020 'state' => 'Inaccessible', 4021 'OSTypeId' => 'Other', 4022 'id' => $machine->id, 4023 'sessionState' => 'Inaccessible', 4024 'accessible' => 0, 4025 'accessError' => array( 4026 'resultCode' => $this->_util_resultCodeText($machine->accessError->resultCode), 4027 'component' => $machine->accessError->component, 4028 'text' => $machine->accessError->text), 4029 'lastStateChange' => 0, 4030 'groups' => array(), 4031 'currentSnapshot' => '' 4032 4033 ); 4034 4035 continue; 4036 } 4037 4038 if($this->settings->phpVboxGroups) { 4039 $groups = explode(',',$machine->getExtraData(vboxconnector::phpVboxGroupKey)); 4040 if(!is_array($groups) || (count($groups) == 1 && !$groups[0])) $groups = array("/"); 4041 } else { 4042 $groups = $machine->groups; 4043 } 4044 4045 usort($groups, 'strnatcasecmp'); 4046 4047 $vmlist[] = array( 4048 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $machine->name) : $machine->name, 4049 'state' => (string)$machine->state, 4050 'OSTypeId' => $machine->getOSTypeId(), 4051 'owner' => (@$this->settings->enforceVMOwnership ? $machine->getExtraData("phpvb/sso/owner") : ''), 4052 'groups' => $groups, 4053 'lastStateChange' => (string)($machine->lastStateChange/1000), 4054 'id' => $machine->id, 4055 'currentStateModified' => $machine->currentStateModified, 4056 'sessionState' => (string)$machine->sessionState, 4057 'currentSnapshotName' => ($machine->currentSnapshot->handle ? $machine->currentSnapshot->name : ''), 4058 'customIcon' => (@$this->settings->enableCustomIcons ? $machine->getExtraData('phpvb/icon') : '') 4059 ); 4060 if($machine->currentSnapshot->handle) $machine->currentSnapshot->releaseRemote(); 4061 4062 4063 } catch (Exception $e) { 4064 4065 if($machine) { 4066 4067 $vmlist[] = array( 4068 'name' => $machine->id, 4069 'state' => 'Inaccessible', 4070 'OSTypeId' => 'Other', 4071 'id' => $machine->id, 4072 'sessionState' => 'Inaccessible', 4073 'lastStateChange' => 0, 4074 'groups' => array(), 4075 'currentSnapshot' => '' 4076 ); 4077 4078 } else { 4079 $this->errors[] = $e; 4080 } 4081 } 4082 4083 try { 4084 $machine->releaseRemote(); 4085 } catch (Exception $e) { } 4086 } 4087 4088 return $vmlist; 4089 4090 } 4091 4092 /** 4093 * Creates a new exception so that input can be debugged. 4094 * 4095 * @param array $args array of arguments. See function body for details. 4096 * @return boolean true on success 4097 */ 4098 public function debugInput($args) { 4099 $this->errors[] = new Exception('debug'); 4100 return true; 4101 } 4102 4103 /** 4104 * Get a list of media registered with VirtualBox 4105 * 4106 * @param unused $args 4107 * @param array $response response data passed byref populated by the function 4108 * @return array of media 4109 */ 4110 public function remote_vboxGetMedia($args) { 4111 4112 // Connect to vboxwebsrv 4113 $this->connect(); 4114 4115 $response = array(); 4116 $mds = array($this->vbox->hardDisks,$this->vbox->DVDImages,$this->vbox->floppyImages); 4117 for($i=0;$i<3;$i++) { 4118 foreach($mds[$i] as $m) { 4119 /* @var $m IMedium */ 4120 $response[] = $this->_mediumGetDetails($m); 4121 $m->releaseRemote(); 4122 } 4123 } 4124 return $response; 4125 } 4126 4127 /** 4128 * Get USB controller information 4129 * 4130 * @param IMachine $m virtual machine instance 4131 * @return array USB controller info 4132 */ 4133 private function _machineGetUSBControllers(&$m) { 4134 4135 /* @var $u IUSBController */ 4136 $controllers = &$m->USBControllers; 4137 4138 $rcons = array(); 4139 foreach($controllers as $c) { 4140 $rcons[] = array( 4141 'name' => $c->name, 4142 'type' => (string)$c->type 4143 ); 4144 $c->releaseRemote(); 4145 } 4146 4147 return $rcons; 4148 } 4149 4150 /** 4151 * Get USB device filters 4152 * 4153 * @param IMachine $m virtual machine instance 4154 * @return array USB device filters 4155 */ 4156 private function _machineGetUSBDeviceFilters(&$m) { 4157 4158 $deviceFilters = array(); 4159 foreach($m->USBDeviceFilters->deviceFilters as $df) { /* @var $df IUSBDeviceFilter */ 4160 4161 $deviceFilters[] = array( 4162 'name' => $df->name, 4163 'active' => $df->active, 4164 'vendorId' => $df->vendorId, 4165 'productId' => $df->productId, 4166 'revision' => $df->revision, 4167 'manufacturer' => $df->manufacturer, 4168 'product' => $df->product, 4169 'serialNumber' => $df->serialNumber, 4170 'port' => $df->port, 4171 'remote' => $df->remote 4172 ); 4173 $df->releaseRemote(); 4174 } 4175 return $deviceFilters; 4176 } 4177 4178 /** 4179 * Return top-level virtual machine or snapshot information 4180 * 4181 * @param IMachine $m virtual machine instance 4182 * @return array vm or snapshot data 4183 */ 4184 private function _machineGetDetails(&$m) { 4185 4186 if($this->settings->phpVboxGroups) { 4187 $groups = explode(',',$m->getExtraData(vboxconnector::phpVboxGroupKey)); 4188 if(!is_array($groups) || (count($groups) == 1 && !$groups[0])) $groups = array("/"); 4189 } else { 4190 $groups = $m->groups; 4191 } 4192 4193 usort($groups, 'strnatcasecmp'); 4194 4195 return array( 4196 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $m->name) : $m->name, 4197 'description' => $m->description, 4198 'groups' => $groups, 4199 'id' => $m->id, 4200 'autostopType' => ($this->settings->vboxAutostartConfig ? (string)$m->autostopType : ''), 4201 'autostartEnabled' => ($this->settings->vboxAutostartConfig && $m->autostartEnabled), 4202 'autostartDelay' => ($this->settings->vboxAutostartConfig ? intval($m->autostartDelay) : '0'), 4203 'settingsFilePath' => $m->settingsFilePath, 4204 'paravirtProvider' => (string)$m->paravirtProvider, 4205 'OSTypeId' => $m->OSTypeId, 4206 'OSTypeDesc' => $this->vbox->getGuestOSType($m->OSTypeId)->description, 4207 'CPUCount' => $m->CPUCount, 4208 'HPETEnabled' => $m->HPETEnabled, 4209 'memorySize' => $m->memorySize, 4210 'VRAMSize' => $m->VRAMSize, 4211 'pointingHIDType' => (string)$m->pointingHIDType, 4212 'keyboardHIDType' => (string)$m->keyboardHIDType, 4213 'accelerate3DEnabled' => $m->accelerate3DEnabled, 4214 'accelerate2DVideoEnabled' => $m->accelerate2DVideoEnabled, 4215 'BIOSSettings' => array( 4216 'ACPIEnabled' => $m->BIOSSettings->ACPIEnabled, 4217 'IOAPICEnabled' => $m->BIOSSettings->IOAPICEnabled, 4218 'timeOffset' => $m->BIOSSettings->timeOffset 4219 ), 4220 'firmwareType' => (string)$m->firmwareType, 4221 'snapshotFolder' => $m->snapshotFolder, 4222 'monitorCount' => $m->monitorCount, 4223 'pageFusionEnabled' => $m->pageFusionEnabled, 4224 'VRDEServer' => (!$m->VRDEServer ? null : array( 4225 'enabled' => $m->VRDEServer->enabled, 4226 'ports' => $m->VRDEServer->getVRDEProperty('TCP/Ports'), 4227 'netAddress' => $m->VRDEServer->getVRDEProperty('TCP/Address'), 4228 'VNCPassword' => $m->VRDEServer->getVRDEProperty('VNCPassword'), 4229 'authType' => (string)$m->VRDEServer->authType, 4230 'authTimeout' => $m->VRDEServer->authTimeout, 4231 'allowMultiConnection' => $m->VRDEServer->allowMultiConnection, 4232 'VRDEExtPack' => (string)$m->VRDEServer->VRDEExtPack 4233 )), 4234 'audioAdapter' => array( 4235 'enabled' => $m->audioAdapter->enabled, 4236 'audioController' => (string)$m->audioAdapter->audioController, 4237 'audioDriver' => (string)$m->audioAdapter->audioDriver, 4238 ), 4239 'RTCUseUTC' => $m->RTCUseUTC, 4240 'EffectiveParavirtProvider' => (string)$m->getEffectiveParavirtProvider(), 4241 'HWVirtExProperties' => array( 4242 'Enabled' => $m->getHWVirtExProperty('Enabled'), 4243 'NestedPaging' => $m->getHWVirtExProperty('NestedPaging'), 4244 'LargePages' => $m->getHWVirtExProperty('LargePages'), 4245 'UnrestrictedExecution' => $m->getHWVirtExProperty('UnrestrictedExecution'), 4246 'VPID' => $m->getHWVirtExProperty('VPID') 4247 ), 4248 'CpuProperties' => array( 4249 'PAE' => $m->getCpuProperty('PAE') 4250 ), 4251 'bootOrder' => $this->_machineGetBootOrder($m), 4252 'chipsetType' => (string)$m->chipsetType, 4253 'GUI' => array( 4254 'FirstRun' => $m->getExtraData('GUI/FirstRun'), 4255 ), 4256 'customIcon' => (@$this->settings->enableCustomIcons ? $m->getExtraData('phpvb/icon') : ''), 4257 'disableHostTimeSync' => intval($m->getExtraData("VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled")), 4258 'CPUExecutionCap' => $m->CPUExecutionCap 4259 ); 4260 4261 } 4262 4263 /** 4264 * Get virtual machine boot order 4265 * 4266 * @param IMachine $m virtual machine instance 4267 * @return array boot order 4268 */ 4269 private function _machineGetBootOrder(&$m) { 4270 $return = array(); 4271 $mbp = $this->vbox->systemProperties->maxBootPosition; 4272 for($i = 0; $i < $mbp; $i ++) { 4273 if(($b = (string)$m->getBootOrder($i + 1)) == 'Null') continue; 4274 $return[] = $b; 4275 } 4276 return $return; 4277 } 4278 4279 /** 4280 * Get serial port configuration for a virtual machine or snapshot 4281 * 4282 * @param IMachine $m virtual machine instance 4283 * @return array serial port info 4284 */ 4285 private function _machineGetSerialPorts(&$m) { 4286 $ports = array(); 4287 $max = $this->vbox->systemProperties->serialPortCount; 4288 for($i = 0; $i < $max; $i++) { 4289 try { 4290 /* @var $p ISerialPort */ 4291 $p = $m->getSerialPort($i); 4292 $ports[] = array( 4293 'slot' => $p->slot, 4294 'enabled' => $p->enabled, 4295 'IOBase' => '0x'.strtoupper(sprintf('%3s',dechex($p->IOBase))), 4296 'IRQ' => $p->IRQ, 4297 'hostMode' => (string)$p->hostMode, 4298 'server' => $p->server, 4299 'path' => $p->path 4300 ); 4301 $p->releaseRemote(); 4302 } catch (Exception $e) { 4303 // Ignore 4304 } 4305 } 4306 return $ports; 4307 } 4308 4309 /** 4310 * Get parallel port configuration for a virtual machine or snapshot 4311 * 4312 * @param IMachine $m virtual machine instance 4313 * @return array parallel port info 4314 */ 4315 private function _machineGetParallelPorts(&$m) { 4316 if(!@$this->settings->enableLPTConfig) return array(); 4317 $ports = array(); 4318 $max = $this->vbox->systemProperties->parallelPortCount; 4319 for($i = 0; $i < $max; $i++) { 4320 try { 4321 /* @var $p IParallelPort */ 4322 $p = $m->getParallelPort($i); 4323 $ports[] = array( 4324 'slot' => $p->slot, 4325 'enabled' => $p->enabled, 4326 'IOBase' => '0x'.strtoupper(sprintf('%3s',dechex($p->IOBase))), 4327 'IRQ' => $p->IRQ, 4328 'path' => $p->path 4329 ); 4330 $p->releaseRemote(); 4331 } catch (Exception $e) { 4332 // Ignore 4333 } 4334 } 4335 return $ports; 4336 } 4337 4338 /** 4339 * Get shared folder configuration for a virtual machine or snapshot 4340 * 4341 * @param IMachine $m virtual machine instance 4342 * @return array shared folder info 4343 */ 4344 private function _machineGetSharedFolders(&$m) { 4345 $sfs = &$m->sharedFolders; 4346 $return = array(); 4347 foreach($sfs as $sf) { /* @var $sf ISharedFolder */ 4348 $return[] = array( 4349 'name' => $sf->name, 4350 'hostPath' => $sf->hostPath, 4351 'accessible' => $sf->accessible, 4352 'writable' => $sf->writable, 4353 'autoMount' => $sf->autoMount, 4354 'lastAccessError' => $sf->lastAccessError, 4355 'type' => 'machine' 4356 ); 4357 } 4358 return $return; 4359 } 4360 4361 /** 4362 * Add encryption password to VM console 4363 * 4364 * @param array $args array of arguments. See function body for details. 4365 * @return true on success 4366 */ 4367 public function remote_consoleAddDiskEncryptionPasswords($args) { 4368 4369 $this->connect(); 4370 4371 /* @var $machine IMachine */ 4372 $machine = $this->vbox->findMachine($args['vm']); 4373 4374 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 4375 $machine->lockMachine($this->session->handle,'Shared'); 4376 4377 $response = array('accepted'=>array(),'failed'=>array(),'errors'=>array()); 4378 4379 foreach($args['passwords'] as $creds) { 4380 try { 4381 $this->session->console->removeDiskEncryptionPassword($creds['id']); 4382 } catch(Exception $e) { 4383 // It may not exist yet 4384 } 4385 4386 try { 4387 $this->session->console->addDiskEncryptionPassword($creds['id'], $creds['password'], (bool)@$args['clearOnSuspend']); 4388 $response['accepted'][] = $creds['id']; 4389 } catch (Exception $e) { 4390 $response['failed'][] = $creds['id']; 4391 $response['errors'][] = $e->getMessage(); 4392 } 4393 } 4394 4395 $this->session->unlockMachine(); 4396 unset($this->session); 4397 $machine->releaseRemote(); 4398 4399 return $response; 4400 } 4401 4402 /** 4403 * Get a list of transient (temporary) shared folders 4404 * 4405 * @param array $args array of arguments. See function body for details. 4406 * @return array of shared folders 4407 */ 4408 public function remote_consoleGetSharedFolders($args) { 4409 4410 $this->connect(); 4411 4412 /* @var $machine IMachine */ 4413 $machine = $this->vbox->findMachine($args['vm']); 4414 4415 // No need to continue if machine is not running 4416 if((string)$machine->state != 'Running') { 4417 $machine->releaseRemote(); 4418 return true; 4419 } 4420 4421 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 4422 $machine->lockMachine($this->session->handle,'Shared'); 4423 4424 $sfs = $this->session->console->sharedFolders; 4425 4426 $response = array(); 4427 4428 foreach($sfs as $sf) { /* @var $sf ISharedFolder */ 4429 4430 $response[] = array( 4431 'name' => $sf->name, 4432 'hostPath' => $sf->hostPath, 4433 'accessible' => $sf->accessible, 4434 'writable' => $sf->writable, 4435 'autoMount' => $sf->autoMount, 4436 'lastAccessError' => $sf->lastAccessError, 4437 'type' => 'transient' 4438 ); 4439 } 4440 4441 $this->session->unlockMachine(); 4442 unset($this->session); 4443 $machine->releaseRemote(); 4444 4445 return $response; 4446 } 4447 4448 /** 4449 * Get VirtualBox Host OS specific directory separator 4450 * 4451 * @return string directory separator string 4452 */ 4453 public function getDsep() { 4454 4455 if(!$this->dsep) { 4456 4457 /* No need to go through vbox if local browser is true */ 4458 if($this->settings->browserLocal) { 4459 4460 $this->dsep = DIRECTORY_SEPARATOR; 4461 4462 } else { 4463 4464 $this->connect(); 4465 4466 if(stripos($this->vbox->host->operatingSystem,'windows') !== false) { 4467 $this->dsep = '\\'; 4468 } else { 4469 $this->dsep = '/'; 4470 } 4471 } 4472 4473 4474 } 4475 4476 return $this->dsep; 4477 } 4478 4479 /** 4480 * Get medium attachment information for all medium attachments in $mas 4481 * 4482 * @param IMediumAttachment[] $mas list of IMediumAttachment instances 4483 * @return array medium attachment info 4484 */ 4485 private function _machineGetMediumAttachments(&$mas) { 4486 4487 $return = array(); 4488 4489 foreach($mas as $ma) { /** @var $ma IMediumAttachment */ 4490 $return[] = array( 4491 'medium' => ($ma->medium->handle ? array('id'=>$ma->medium->id) : null), 4492 'controller' => $ma->controller, 4493 'port' => $ma->port, 4494 'device' => $ma->device, 4495 'type' => (string)$ma->type, 4496 'passthrough' => $ma->passthrough, 4497 'temporaryEject' => $ma->temporaryEject, 4498 'nonRotational' => $ma->nonRotational, 4499 'hotPluggable' => $ma->hotPluggable, 4500 ); 4501 } 4502 4503 // sort by port then device 4504 usort($return,function($a,$b){if($a["port"] == $b["port"]) { if($a["device"] < $b["device"]) { return -1; } if($a["device"] > $b["device"]) { return 1; } return 0; } if($a["port"] < $b["port"]) { return -1; } return 1;}); 4505 4506 return $return; 4507 } 4508 4509 /** 4510 * Save snapshot details ( description or name) 4511 * 4512 * @param array $args array of arguments. See function body for details. 4513 * @return boolean true on success 4514 */ 4515 public function remote_snapshotSave($args) { 4516 4517 // Connect to vboxwebsrv 4518 $this->connect(); 4519 4520 /* @var $vm IMachine */ 4521 $vm = $this->vbox->findMachine($args['vm']); 4522 4523 /* @var $snapshot ISnapshot */ 4524 $snapshot = $vm->findSnapshot($args['snapshot']); 4525 $snapshot->name = $args['name']; 4526 $snapshot->description = $args['description']; 4527 4528 // cleanup 4529 $snapshot->releaseRemote(); 4530 $vm->releaseRemote(); 4531 4532 return true; 4533 } 4534 4535 /** 4536 * Get snapshot details 4537 * 4538 * @param array $args array of arguments. See function body for details. 4539 * @return array containing snapshot details 4540 */ 4541 public function remote_snapshotGetDetails($args) { 4542 4543 // Connect to vboxwebsrv 4544 $this->connect(); 4545 4546 /* @var $vm IMachine */ 4547 $vm = $this->vbox->findMachine($args['vm']); 4548 4549 /* @var $snapshot ISnapshot */ 4550 $snapshot = $vm->findSnapshot($args['snapshot']); 4551 4552 $response = $this->_snapshotGetDetails($snapshot,false); 4553 $response['machine'] = $this->remote_machineGetDetails(array(),$snapshot->machine); 4554 4555 // cleanup 4556 $snapshot->releaseRemote(); 4557 $vm->releaseRemote(); 4558 4559 return $response; 4560 4561 } 4562 4563 /** 4564 * Restore a snapshot 4565 * 4566 * @param array $args array of arguments. See function body for details. 4567 * @return array response data containing progress operation id 4568 */ 4569 public function remote_snapshotRestore($args) { 4570 4571 // Connect to vboxwebsrv 4572 $this->connect(); 4573 4574 $progress = $this->session = null; 4575 4576 try { 4577 4578 // Open session to machine 4579 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 4580 4581 /* @var $machine IMachine */ 4582 $machine = $this->vbox->findMachine($args['vm']); 4583 $machine->lockMachine($this->session->handle, 'Write'); 4584 4585 /* @var $snapshot ISnapshot */ 4586 $snapshot = $this->session->machine->findSnapshot($args['snapshot']); 4587 4588 /* @var $progress IProgress */ 4589 $progress = $this->session->machine->restoreSnapshot($snapshot->handle); 4590 4591 $snapshot->releaseRemote(); 4592 $machine->releaseRemote(); 4593 4594 // Does an exception exist? 4595 try { 4596 if($progress->errorInfo->handle) { 4597 $this->errors[] = new Exception($progress->errorInfo->text); 4598 $progress->releaseRemote(); 4599 return false; 4600 } 4601 } catch (Exception $null) {} 4602 4603 $this->_util_progressStore($progress); 4604 4605 } catch (Exception $e) { 4606 4607 $this->errors[] = $e; 4608 4609 if($this->session->handle) { 4610 try{$this->session->unlockMachine();}catch(Exception $e){} 4611 } 4612 return false; 4613 } 4614 4615 return array('progress' => $progress->handle); 4616 4617 } 4618 4619 /** 4620 * Delete a snapshot 4621 * 4622 * @param array $args array of arguments. See function body for details. 4623 * @return array response data containing progress operation id 4624 */ 4625 public function remote_snapshotDelete($args) { 4626 4627 // Connect to vboxwebsrv 4628 $this->connect(); 4629 4630 $progress = $this->session = null; 4631 4632 try { 4633 4634 // Open session to machine 4635 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 4636 4637 /* @var $machine IMachine */ 4638 $machine = $this->vbox->findMachine($args['vm']); 4639 $machine->lockMachine($this->session->handle, 'Shared'); 4640 4641 /* @var $progress IProgress */ 4642 $progress = $this->session->machine->deleteSnapshot($args['snapshot']); 4643 4644 $machine->releaseRemote(); 4645 4646 // Does an exception exist? 4647 try { 4648 if($progress->errorInfo->handle) { 4649 $this->errors[] = new Exception($progress->errorInfo->text); 4650 $progress->releaseRemote(); 4651 return false; 4652 } 4653 } catch (Exception $null) {} 4654 4655 $this->_util_progressStore($progress); 4656 4657 4658 } catch (Exception $e) { 4659 4660 $this->errors[] = $e; 4661 4662 if($this->session->handle) { 4663 try{$this->session->unlockMachine();$this->session=null;}catch(Exception $e){} 4664 } 4665 4666 return false; 4667 } 4668 4669 return array('progress' => $progress->handle); 4670 4671 } 4672 4673 /** 4674 * Take a snapshot 4675 * 4676 * @param array $args array of arguments. See function body for details. 4677 * @return array response data containing progress operation id 4678 */ 4679 public function remote_snapshotTake($args) { 4680 4681 // Connect to vboxwebsrv 4682 $this->connect(); 4683 4684 /* @var $machine IMachine */ 4685 $machine = $this->vbox->findMachine($args['vm']); 4686 4687 $progress = $this->session = null; 4688 4689 try { 4690 4691 // Open session to machine 4692 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 4693 $machine->lockMachine($this->session->handle, ((string)$machine->sessionState == 'Unlocked' ? 'Write' : 'Shared')); 4694 4695 /* @var $progress IProgress */ 4696 list($progress, $snapshotId) = $this->session->machine->takeSnapshot($args['name'], $args['description'], null); 4697 4698 // Does an exception exist? 4699 try { 4700 if($progress->errorInfo->handle) { 4701 $this->errors[] = new Exception($progress->errorInfo->text); 4702 $progress->releaseRemote(); 4703 try{$this->session->unlockMachine(); $this->session=null;}catch(Exception $ed){} 4704 return false; 4705 } 4706 } catch (Exception $null) {} 4707 4708 4709 $this->_util_progressStore($progress); 4710 4711 } catch (Exception $e) { 4712 4713 if(!$progress->handle && $this->session->handle) { 4714 try{$this->session->unlockMachine();$this->session=null;}catch(Exception $e){} 4715 } 4716 4717 return false; 4718 } 4719 4720 return array('progress' => $progress->handle); 4721 4722 } 4723 4724 /** 4725 * Get a list of snapshots for a machine 4726 * 4727 * @param array $args array of arguments. See function body for details. 4728 * @return array list of snapshots 4729 */ 4730 public function remote_machineGetSnapshots($args) { 4731 4732 // Connect to vboxwebsrv 4733 $this->connect(); 4734 4735 /* @var $machine IMachine */ 4736 $machine = $this->vbox->findMachine($args['vm']); 4737 4738 $response = array('vm' => $args['vm'], 4739 'snapshot' => array(), 4740 'currentSnapshotId' => null); 4741 4742 /* No snapshots? Empty array */ 4743 if($machine->snapshotCount < 1) { 4744 return $response; 4745 } else { 4746 4747 /* @var $s ISnapshot */ 4748 $s = $machine->findSnapshot(null); 4749 $response['snapshot'] = $this->_snapshotGetDetails($s,true); 4750 $s->releaseRemote(); 4751 } 4752 4753 $response['currentSnapshotId'] = ($machine->currentSnapshot->handle ? $machine->currentSnapshot->id : ''); 4754 if($machine->currentSnapshot->handle) $machine->currentSnapshot->releaseRemote(); 4755 $machine->releaseRemote(); 4756 4757 return $response; 4758 } 4759 4760 4761 /** 4762 * Return details about snapshot $s 4763 * 4764 * @param ISnapshot $s snapshot instance 4765 * @param boolean $sninfo traverse child snapshots 4766 * @return array snapshot info 4767 */ 4768 private function _snapshotGetDetails(&$s,$sninfo=false) { 4769 4770 $children = array(); 4771 4772 if($sninfo) 4773 foreach($s->children as $c) { /* @var $c ISnapshot */ 4774 $children[] = $this->_snapshotGetDetails($c, true); 4775 $c->releaseRemote(); 4776 } 4777 4778 // Avoid multiple soap calls 4779 $timestamp = (string)$s->timeStamp; 4780 4781 return array( 4782 'id' => $s->id, 4783 'name' => $s->name, 4784 'description' => $s->description, 4785 'timeStamp' => floor($timestamp/1000), 4786 'timeStampSplit' => $this->_util_splitTime(time() - floor($timestamp/1000)), 4787 'online' => $s->online 4788 ) + ( 4789 ($sninfo ? array('children' => $children) : array()) 4790 ); 4791 } 4792 4793 /** 4794 * Return details about storage controllers for machine $m 4795 * 4796 * @param IMachine $m virtual machine instance 4797 * @return array storage controllers' details 4798 */ 4799 private function _machineGetStorageControllers(&$m) { 4800 4801 $sc = array(); 4802 $scs = $m->storageControllers; 4803 4804 foreach($scs as $c) { /* @var $c IStorageController */ 4805 $sc[] = array( 4806 'name' => $c->name, 4807 'maxDevicesPerPortCount' => $c->maxDevicesPerPortCount, 4808 'useHostIOCache' => $c->useHostIOCache, 4809 'minPortCount' => $c->minPortCount, 4810 'maxPortCount' => $c->maxPortCount, 4811 'portCount' => $c->portCount, 4812 'bus' => (string)$c->bus, 4813 'controllerType' => (string)$c->controllerType, 4814 'mediumAttachments' => $this->_machineGetMediumAttachments($m->getMediumAttachmentsOfController($c->name), $m->id) 4815 ); 4816 $c->releaseRemote(); 4817 } 4818 4819 for($i = 0; $i < count($sc); $i++) { 4820 4821 for($a = 0; $a < count($sc[$i]['mediumAttachments']); $a++) { 4822 4823 // Value of '' means it is not applicable 4824 $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = ''; 4825 4826 // Only valid for HardDisks 4827 if($sc[$i]['mediumAttachments'][$a]['type'] != 'HardDisk') continue; 4828 4829 // Get appropriate key 4830 $xtra = $this->_util_getIgnoreFlushKey($sc[$i]['mediumAttachments'][$a]['port'], $sc[$i]['mediumAttachments'][$a]['device'], $sc[$i]['controllerType']); 4831 4832 // No such setting for this bus type 4833 if(!$xtra) continue; 4834 4835 $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = $m->getExtraData($xtra); 4836 4837 if(trim($sc[$i]['mediumAttachments'][$a]['ignoreFlush']) === '') 4838 $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = 1; 4839 else 4840 $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = $sc[$i]['mediumAttachments'][$a]['ignoreFlush']; 4841 4842 } 4843 } 4844 4845 return $sc; 4846 } 4847 4848 /** 4849 * Check medium encryption password 4850 * 4851 * @param array $args array of arguments. See function body for details. 4852 * @return array response data 4853 */ 4854 public function remote_mediumCheckEncryptionPassword($args) { 4855 4856 // Connect to vboxwebsrv 4857 $this->connect(); 4858 4859 $m = $this->vbox->openMedium($args['medium'],'HardDisk','ReadWrite',false); 4860 4861 $retval = $m->checkEncryptionPassword($args['password']); 4862 4863 $m->releaseRemote(); 4864 4865 return $retval; 4866 4867 } 4868 4869 /** 4870 * Change medium encryption 4871 * 4872 * @param array $args array of arguments. See function body for details. 4873 * @return array response data containing progress id or true 4874 */ 4875 public function remote_mediumChangeEncryption($args) { 4876 4877 // Connect to vboxwebsrv 4878 $this->connect(); 4879 4880 $m = $this->vbox->openMedium($args['medium'], 'HardDisk', 'ReadWrite', false); 4881 4882 /* @var $progress IProgress */ 4883 $progress = $m->changeEncryption($args['old_password'], 4884 $args['cipher'], $args['password'], $args['id']); 4885 4886 // Does an exception exist? 4887 try { 4888 if($progress->errorInfo->handle) { 4889 $this->errors[] = new Exception($progress->errorInfo->text); 4890 $progress->releaseRemote(); 4891 $m->releaseRemote(); 4892 return false; 4893 } 4894 } catch (Exception $null) { 4895 } 4896 4897 if($args['waitForCompletion']) { 4898 $progress->waitForCompletion(-1); 4899 $progress->releaseRemote(); 4900 $m->releaseRemote(); 4901 return true; 4902 } 4903 4904 $this->_util_progressStore($progress); 4905 4906 return array('progress' => $progress->handle); 4907 4908 } 4909 4910 /** 4911 * Resize a medium. Currently unimplemented in GUI. 4912 * 4913 * @param array $args array of arguments. See function body for details. 4914 * @return array response data containing progress id 4915 */ 4916 public function remote_mediumResize($args) { 4917 4918 // Connect to vboxwebsrv 4919 $this->connect(); 4920 4921 $m = $this->vbox->openMedium($args['medium'], 'HardDisk', 'ReadWrite', false); 4922 4923 /* @var $progress IProgress */ 4924 $progress = $m->resize($args['bytes']); 4925 4926 // Does an exception exist? 4927 try { 4928 if($progress->errorInfo->handle) { 4929 $this->errors[] = new Exception($progress->errorInfo->text); 4930 $progress->releaseRemote(); 4931 return false; 4932 } 4933 } catch (Exception $null) { 4934 } 4935 4936 $this->_util_progressStore($progress); 4937 4938 return array('progress' => $progress->handle); 4939 4940 } 4941 4942 /** 4943 * Clone a medium 4944 * 4945 * @param array $args array of arguments. See function body for details. 4946 * @return array response data containing progress id 4947 */ 4948 public function remote_mediumCloneTo($args) { 4949 4950 // Connect to vboxwebsrv 4951 $this->connect(); 4952 4953 $format = strtoupper($args['format']); 4954 /* @var $target IMedium */ 4955 $target = $this->vbox->createMedium($format, $args['location'], 'ReadWrite', 'HardDisk'); 4956 $mid = $target->id; 4957 4958 /* @var $src IMedium */ 4959 $src = $this->vbox->openMedium($args['src'], 'HardDisk', 'ReadWrite', false); 4960 4961 $type = array(($args['type'] == 'fixed' ? 'Fixed' : 'Standard')); 4962 if($args['split']) $type[] = 'VmdkSplit2G'; 4963 4964 /* @var $progress IProgress */ 4965 $progress = $src->cloneTo($target->handle,$type,null); 4966 4967 $src->releaseRemote(); 4968 $target->releaseRemote(); 4969 4970 // Does an exception exist? 4971 try { 4972 if($progress->errorInfo->handle) { 4973 $this->errors[] = new Exception($progress->errorInfo->text); 4974 $progress->releaseRemote(); 4975 return false; 4976 } 4977 } catch (Exception $null) {} 4978 4979 $this->_util_progressStore($progress); 4980 4981 return array('progress' => $progress->handle, 'id' => $mid); 4982 4983 } 4984 4985 /** 4986 * Set medium to a specific type 4987 * 4988 * @param array $args array of arguments. See function body for details. 4989 * @return boolean true on success 4990 */ 4991 public function remote_mediumSetType($args) { 4992 4993 // Connect to vboxwebsrv 4994 $this->connect(); 4995 4996 /* @var $m IMedium */ 4997 $m = $this->vbox->openMedium($args['medium'], 'HardDisk', 'ReadWrite', false); 4998 $m->type = $args['type']; 4999 $m->releaseRemote(); 5000 5001 return true; 5002 } 5003 5004 /** 5005 * Add iSCSI medium 5006 * 5007 * @param array $args array of arguments. See function body for details. 5008 * @return response data 5009 */ 5010 public function remote_mediumAddISCSI($args) { 5011 5012 // Connect to vboxwebsrv 5013 $this->connect(); 5014 5015 // {'server':server,'port':port,'intnet':intnet,'target':target,'lun':lun,'enclun':enclun,'targetUser':user,'targetPass':pass} 5016 5017 // Fix LUN 5018 $args['lun'] = intval($args['lun']); 5019 if($args['enclun']) $args['lun'] = 'enc'.$args['lun']; 5020 5021 // Compose name 5022 $name = $args['server'].'|'.$args['target']; 5023 if($args['lun'] != 0 && $args['lun'] != 'enc0') 5024 $name .= '|'.$args['lun']; 5025 5026 // Create disk 5027 /* @var $hd IMedium */ 5028 $hd = $this->vbox->createMedium('iSCSI',$name, 'ReadWrite', 'HardDisk'); 5029 5030 if($args['port']) $args['server'] .= ':'.intval($args['port']); 5031 5032 $arrProps = array(); 5033 5034 $arrProps["TargetAddress"] = $args['server']; 5035 $arrProps["TargetName"] = $args['target']; 5036 $arrProps["LUN"] = $args['lun']; 5037 if($args['targetUser']) $arrProps["InitiatorUsername"] = $args['targetUser']; 5038 if($args['targetPass']) $arrProps["InitiatorSecret"] = $args['targetPass']; 5039 if($args['intnet']) $arrProps["HostIPStack"] = '0'; 5040 5041 $hd->setProperties(array_keys($arrProps),array_values($arrProps)); 5042 5043 $hdid = $hd->id; 5044 $hd->releaseRemote(); 5045 5046 return array('id' => $hdid); 5047 } 5048 5049 /** 5050 * Add existing medium by file location 5051 * 5052 * @param array $args array of arguments. See function body for details. 5053 * @return resposne data containing new medium's id 5054 */ 5055 public function remote_mediumAdd($args) { 5056 5057 // Connect to vboxwebsrv 5058 $this->connect(); 5059 5060 /* @var $m IMedium */ 5061 $m = $this->vbox->openMedium($args['path'], $args['type'], 'ReadWrite', false); 5062 5063 $mid = $m->id; 5064 $m->releaseRemote(); 5065 5066 return array('id'=>$mid); 5067 } 5068 5069 /** 5070 * Get VirtualBox generated machine configuration file name 5071 * 5072 * @param array $args array of arguments. See function body for details. 5073 * @return string filename 5074 */ 5075 public function remote_vboxGetComposedMachineFilename($args) { 5076 5077 // Connect to vboxwebsrv 5078 $this->connect(); 5079 5080 return $this->vbox->composeMachineFilename($args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$this->vbox->systemProperties->defaultMachineFolder,null); 5081 5082 } 5083 5084 /** 5085 * Create base storage medium (virtual hard disk) 5086 * 5087 * @param array $args array of arguments. See function body for details. 5088 * @return response data containing progress id 5089 */ 5090 public function remote_mediumCreateBaseStorage($args) { 5091 5092 // Connect to vboxwebsrv 5093 $this->connect(); 5094 5095 $format = strtoupper($args['format']); 5096 5097 $type = array(($args['type'] == 'fixed' ? 'Fixed' : 'Standard')); 5098 if($args['split']) $type[] = 'VmdkSplit2G'; 5099 5100 /* @var $hd IMedium */ 5101 $hd = $this->vbox->createMedium($format, $args['file'], 'ReadWrite', 'HardDisk'); 5102 5103 /* @var $progress IProgress */ 5104 $progress = $hd->createBaseStorage(intval($args['size'])*1024*1024,$type); 5105 5106 // Does an exception exist? 5107 try { 5108 if($progress->errorInfo->handle) { 5109 $this->errors[] = new Exception($progress->errorInfo->text); 5110 $progress->releaseRemote(); 5111 return false; 5112 } 5113 } catch (Exception $null) {} 5114 5115 $this->_util_progressStore($progress); 5116 5117 $hd->releaseRemote(); 5118 5119 return array('progress' => $progress->handle); 5120 5121 } 5122 5123 /** 5124 * Release medium from all attachments 5125 * 5126 * @param array $args array of arguments. See function body for details. 5127 * @return boolean true 5128 */ 5129 public function remote_mediumRelease($args) { 5130 5131 // Connect to vboxwebsrv 5132 $this->connect(); 5133 5134 /* @var $m IMedium */ 5135 $m = $this->vbox->openMedium($args['medium'],$args['type'], 'ReadWrite', false); 5136 $mediumid = $m->id; 5137 5138 // connected to... 5139 $machines = $m->machineIds; 5140 $released = array(); 5141 foreach($machines as $uuid) { 5142 5143 // Find medium attachment 5144 try { 5145 /* @var $mach IMachine */ 5146 $mach = $this->vbox->findMachine($uuid); 5147 } catch (Exception $e) { 5148 $this->errors[] = $e; 5149 continue; 5150 } 5151 $attach = $mach->mediumAttachments; 5152 $remove = array(); 5153 foreach($attach as $a) { 5154 if($a->medium->handle && $a->medium->id == $mediumid) { 5155 $remove[] = array( 5156 'controller' => $a->controller, 5157 'port' => $a->port, 5158 'device' => $a->device); 5159 break; 5160 } 5161 } 5162 // save state 5163 $state = (string)$mach->sessionState; 5164 5165 if(!count($remove)) continue; 5166 5167 $released[] = $uuid; 5168 5169 // create session 5170 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 5171 5172 // Hard disk requires machine to be stopped 5173 if($args['type'] == 'HardDisk' || $state == 'Unlocked') { 5174 5175 $mach->lockMachine($this->session->handle, 'Write'); 5176 5177 } else { 5178 5179 $mach->lockMachine($this->session->handle, 'Shared'); 5180 5181 } 5182 5183 foreach($remove as $r) { 5184 if($args['type'] == 'HardDisk') { 5185 $this->session->machine->detachDevice($r['controller'],$r['port'],$r['device']); 5186 } else { 5187 $this->session->machine->mountMedium($r['controller'],$r['port'],$r['device'],null,true); 5188 } 5189 } 5190 5191 $this->session->machine->saveSettings(); 5192 $this->session->machine->releaseRemote(); 5193 $this->session->unlockMachine(); 5194 unset($this->session); 5195 $mach->releaseRemote(); 5196 5197 } 5198 $m->releaseRemote(); 5199 5200 return true; 5201 } 5202 5203 /** 5204 * Remove a medium 5205 * 5206 * @param array $args array of arguments. See function body for details. 5207 * @return response data possibly containing progress operation id 5208 */ 5209 public function remote_mediumRemove($args) { 5210 5211 // Connect to vboxwebsrv 5212 $this->connect(); 5213 5214 if(!$args['type']) $args['type'] = 'HardDisk'; 5215 5216 /* @var $m IMedium */ 5217 $m = $this->vbox->openMedium($args['medium'],$args['type'], 'ReadWrite', false); 5218 5219 if($args['delete'] && @$this->settings->deleteOnRemove && (string)$m->deviceType == 'HardDisk') { 5220 5221 /* @var $progress IProgress */ 5222 $progress = $m->deleteStorage(); 5223 5224 $m->releaseRemote(); 5225 5226 // Does an exception exist? 5227 try { 5228 if($progress->errorInfo->handle) { 5229 $this->errors[] = new Exception($progress->errorInfo->text); 5230 $progress->releaseRemote(); 5231 return false; 5232 } 5233 } catch (Exception $null) { } 5234 5235 $this->_util_progressStore($progress); 5236 return array('progress' => $progress->handle); 5237 5238 } else { 5239 $m->close(); 5240 $m->releaseRemote(); 5241 } 5242 5243 return true; 5244 } 5245 5246 /** 5247 * Get a list of recent media 5248 * 5249 * @param array $args array of arguments. See function body for details. 5250 * @return array of recent media 5251 */ 5252 public function remote_vboxRecentMediaGet($args) { 5253 5254 // Connect to vboxwebsrv 5255 $this->connect(); 5256 5257 $mlist = array(); 5258 foreach(array( 5259 array('type'=>'HardDisk','key'=>'GUI/RecentListHD'), 5260 array('type'=>'DVD','key'=>'GUI/RecentListCD'), 5261 array('type'=>'Floppy','key'=>'GUI/RecentListFD')) as $r) { 5262 $list = $this->vbox->getExtraData($r['key']); 5263 $mlist[$r['type']] = array_filter(explode(';', trim($list,';'))); 5264 } 5265 return $mlist; 5266 } 5267 5268 /** 5269 * Get a list of recent media paths 5270 * 5271 * @param array $args array of arguments. See function body for details. 5272 * @return array of recent media paths 5273 */ 5274 public function remote_vboxRecentMediaPathsGet($args) { 5275 5276 // Connect to vboxwebsrv 5277 $this->connect(); 5278 5279 $mlist = array(); 5280 foreach(array( 5281 array('type'=>'HardDisk','key'=>'GUI/RecentFolderHD'), 5282 array('type'=>'DVD','key'=>'GUI/RecentFolderCD'), 5283 array('type'=>'Floppy','key'=>'GUI/RecentFolderFD')) as $r) { 5284 $mlist[$r['type']] = $this->vbox->getExtraData($r['key']); 5285 } 5286 return $mlist; 5287 } 5288 5289 5290 /** 5291 * Update recent medium path list 5292 * 5293 * @param array $args array of arguments. See function body for details. 5294 * @return boolean true on success 5295 */ 5296 public function remote_vboxRecentMediaPathSave($args) { 5297 5298 // Connect to vboxwebsrv 5299 $this->connect(); 5300 5301 $types = array( 5302 'HardDisk'=>'GUI/RecentFolderHD', 5303 'DVD'=>'GUI/RecentFolderCD', 5304 'Floppy'=>'GUI/RecentFolderFD' 5305 ); 5306 5307 $this->vbox->setExtraData($types[$args['type']], $args['folder']); 5308 5309 return true; 5310 } 5311 5312 /** 5313 * Update recent media list 5314 * 5315 * @param array $args array of arguments. See function body for details. 5316 * @return boolean true on success 5317 */ 5318 public function remote_vboxRecentMediaSave($args) { 5319 5320 // Connect to vboxwebsrv 5321 $this->connect(); 5322 5323 $types = array( 5324 'HardDisk'=>'GUI/RecentListHD', 5325 'DVD'=>'GUI/RecentListCD', 5326 'Floppy'=>'GUI/RecentListFD' 5327 ); 5328 5329 $this->vbox->setExtraData($types[$args['type']], implode(';',array_unique($args['list'])).';'); 5330 5331 return true; 5332 5333 } 5334 5335 /** 5336 * Mount a medium on the VM 5337 * 5338 * @param array $args array of arguments. See function body for details. 5339 * @return boolean true on success 5340 */ 5341 public function remote_mediumMount($args) { 5342 5343 // Connect to vboxwebsrv 5344 $this->connect(); 5345 5346 // Find medium attachment 5347 /* @var $machine IMachine */ 5348 $machine = $this->vbox->findMachine($args['vm']); 5349 $state = (string)$machine->sessionState; 5350 5351 // create session 5352 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 5353 5354 if($state == 'Unlocked') { 5355 $machine->lockMachine($this->session->handle,'Write'); 5356 $save = true; // force save on closed session as it is not a "run-time" change 5357 } else { 5358 5359 $machine->lockMachine($this->session->handle, 'Shared'); 5360 } 5361 5362 // Empty medium / eject 5363 if($args['medium'] == 0) { 5364 $med = null; 5365 } else { 5366 // Host drive 5367 if(strtolower($args['medium']['hostDrive']) == 'true' || $args['medium']['hostDrive'] === true) { 5368 // CD / DVD Drive 5369 if($args['medium']['deviceType'] == 'DVD') { 5370 $drives = $this->vbox->host->DVDDrives; 5371 // floppy drives 5372 } else { 5373 $drives = $this->vbox->host->floppyDrives; 5374 } 5375 foreach($drives as $m) { /* @var $m IMedium */ 5376 if($m->id == $args['medium']['id']) { 5377 /* @var $med IMedium */ 5378 $med = &$m; 5379 break; 5380 } 5381 $m->releaseRemote(); 5382 } 5383 // Normal medium 5384 } else { 5385 /* @var $med IMedium */ 5386 $med = $this->vbox->openMedium($args['medium']['location'],$args['medium']['deviceType'],'ReadWrite',false); 5387 } 5388 } 5389 5390 $this->session->machine->mountMedium($args['controller'],$args['port'],$args['device'],(is_object($med) ? $med->handle : null),true); 5391 5392 if(is_object($med)) $med->releaseRemote(); 5393 5394 if($save) $this->session->machine->saveSettings(); 5395 5396 $this->session->unlockMachine(); 5397 $machine->releaseRemote(); 5398 unset($this->session); 5399 5400 return true; 5401 } 5402 5403 /** 5404 * Get medium details 5405 * 5406 * @param IMedium $m medium instance 5407 * @return array medium details 5408 */ 5409 private function _mediumGetDetails(&$m) { 5410 5411 $children = array(); 5412 $attachedTo = array(); 5413 $machines = $m->machineIds; 5414 $hasSnapshots = 0; 5415 5416 foreach($m->children as $c) { /* @var $c IMedium */ 5417 $children[] = $this->_mediumGetDetails($c); 5418 $c->releaseRemote(); 5419 } 5420 5421 foreach($machines as $mid) { 5422 $sids = $m->getSnapshotIds($mid); 5423 try { 5424 /* @var $mid IMachine */ 5425 $mid = $this->vbox->findMachine($mid); 5426 } catch (Exception $e) { 5427 $attachedTo[] = array('machine' => $mid .' ('.$e->getMessage().')', 'snapshots' => array()); 5428 continue; 5429 } 5430 5431 $c = count($sids); 5432 $hasSnapshots = max($hasSnapshots,$c); 5433 for($i = 0; $i < $c; $i++) { 5434 if($sids[$i] == $mid->id) { 5435 unset($sids[$i]); 5436 } else { 5437 try { 5438 /* @var $sn ISnapshot */ 5439 $sn = $mid->findSnapshot($sids[$i]); 5440 $sids[$i] = $sn->name; 5441 $sn->releaseRemote(); 5442 } catch(Exception $e) { } 5443 } 5444 } 5445 $hasSnapshots = (count($sids) ? 1 : 0); 5446 $attachedTo[] = array('machine'=>$mid->name,'snapshots'=>$sids); 5447 $mid->releaseRemote(); 5448 } 5449 5450 // For $fixed value 5451 $mvenum = new MediumVariant(null, null); 5452 $variant = 0; 5453 5454 foreach($m->variant as $mv) { 5455 $variant += $mvenum->ValueMap[(string)$mv]; 5456 } 5457 5458 // Encryption settings 5459 $encryptionSettings = null; 5460 if((string)$m->deviceType == 'HardDisk') { 5461 try { 5462 list($id, $cipher) = $m->getEncryptionSettings(); 5463 if($id) { 5464 $encryptionSettings = array( 5465 'id' => $id, 5466 'cipher' => $cipher, 5467 ); 5468 } 5469 } catch (Exception $e) { 5470 // Pass. Encryption is not configured 5471 } 5472 5473 } 5474 return array( 5475 'id' => $m->id, 5476 'description' => $m->description, 5477 'state' => (string)$m->refreshState(), 5478 'location' => $m->location, 5479 'name' => $m->name, 5480 'deviceType' => (string)$m->deviceType, 5481 'hostDrive' => $m->hostDrive, 5482 'size' => (string)$m->size, /* (string) to support large disks. Bypass integer limit */ 5483 'format' => $m->format, 5484 'type' => (string)$m->type, 5485 'parent' => (((string)$m->deviceType == 'HardDisk' && $m->parent->handle) ? $m->parent->id : null), 5486 'children' => $children, 5487 'base' => (((string)$m->deviceType == 'HardDisk' && $m->base->handle) ? $m->base->id : null), 5488 'readOnly' => $m->readOnly, 5489 'logicalSize' => ($m->logicalSize/1024)/1024, 5490 'autoReset' => $m->autoReset, 5491 'hasSnapshots' => $hasSnapshots, 5492 'lastAccessError' => $m->lastAccessError, 5493 'variant' => $variant, 5494 'machineIds' => array(), 5495 'attachedTo' => $attachedTo, 5496 'encryptionSettings' => $encryptionSettings 5497 ); 5498 5499 } 5500 5501 /** 5502 * Store a progress operation so that its status can be polled via progressGet() 5503 * 5504 * @param IProgress $progress progress operation instance 5505 * @return string progress operation handle / id 5506 */ 5507 private function _util_progressStore(&$progress) { 5508 5509 /* Store vbox and session handle */ 5510 $this->persistentRequest['vboxHandle'] = $this->vbox->handle; 5511 if($this->session->handle) { 5512 $this->persistentRequest['sessionHandle'] = $this->session->handle; 5513 } 5514 5515 /* Store server if multiple servers are configured */ 5516 if(@is_array($this->settings->servers) && count($this->settings->servers) > 1) 5517 $this->persistentRequest['vboxServer'] = $this->settings->name; 5518 5519 return $progress->handle; 5520 } 5521 5522 /** 5523 * Get VirtualBox system properties 5524 * @param array $args array of arguments. See function body for details. 5525 * @return array of system properties 5526 */ 5527 public function remote_vboxSystemPropertiesGet($args) { 5528 5529 // Connect to vboxwebsrv 5530 $this->connect(); 5531 5532 $mediumFormats = array(); 5533 5534 // Shorthand 5535 $sp = $this->vbox->systemProperties; 5536 5537 // capabilities 5538 $mfCap = new MediumFormatCapabilities(null,''); 5539 foreach($sp->mediumFormats as $mf) { /* @var $mf IMediumFormat */ 5540 $exts = $mf->describeFileExtensions(); 5541 $dtypes = array(); 5542 foreach($exts[1] as $t) $dtypes[] = (string)$t; 5543 $caps = array(); 5544 foreach($mf->capabilities as $c) { 5545 $caps[] = (string)$c; 5546 } 5547 5548 $mediumFormats[] = array('id'=>$mf->id,'name'=>$mf->name,'extensions'=>array_map('strtolower',$exts[0]),'deviceTypes'=>$dtypes,'capabilities'=>$caps); 5549 5550 } 5551 5552 $scs = array(); 5553 5554 $scts = array('LsiLogic', 5555 'BusLogic', 5556 'IntelAhci', 5557 'PIIX4', 5558 'ICH6', 5559 'I82078', 5560 'USB'); 5561 5562 foreach($scts as $t) { 5563 $scs[$t] = $sp->getStorageControllerHotplugCapable($t); 5564 } 5565 5566 return array( 5567 'minGuestRAM' => (string)$sp->minGuestRAM, 5568 'maxGuestRAM' => (string)$sp->maxGuestRAM, 5569 'minGuestVRAM' => (string)$sp->minGuestVRAM, 5570 'maxGuestVRAM' => (string)$sp->maxGuestVRAM, 5571 'minGuestCPUCount' => (string)$sp->minGuestCPUCount, 5572 'maxGuestCPUCount' => (string)$sp->maxGuestCPUCount, 5573 'autostartDatabasePath' => (@$this->settings->vboxAutostartConfig ? $sp->autostartDatabasePath : ''), 5574 'infoVDSize' => (string)$sp->infoVDSize, 5575 'networkAdapterCount' => 8, // static value for now 5576 'maxBootPosition' => (string)$sp->maxBootPosition, 5577 'defaultMachineFolder' => (string)$sp->defaultMachineFolder, 5578 'defaultHardDiskFormat' => (string)$sp->defaultHardDiskFormat, 5579 'homeFolder' => $this->vbox->homeFolder, 5580 'VRDEAuthLibrary' => (string)$sp->VRDEAuthLibrary, 5581 'defaultAudioDriver' => (string)$sp->defaultAudioDriver, 5582 'defaultVRDEExtPack' => $sp->defaultVRDEExtPack, 5583 'serialPortCount' => $sp->serialPortCount, 5584 'parallelPortCount' => $sp->parallelPortCount, 5585 'mediumFormats' => $mediumFormats, 5586 'scs' => $scs 5587 ); 5588 } 5589 5590 /** 5591 * Get a list of VM log file names 5592 * 5593 * @param array $args array of arguments. See function body for details. 5594 * @return array of log file names 5595 */ 5596 public function remote_machineGetLogFilesList($args) { 5597 5598 // Connect to vboxwebsrv 5599 $this->connect(); 5600 5601 /* @var $m IMachine */ 5602 $m = $this->vbox->findMachine($args['vm']); 5603 5604 $logs = array(); 5605 5606 try { $i = 0; while($l = $m->queryLogFilename($i++)) $logs[] = $l; 5607 } catch (Exception $null) {} 5608 5609 $lf = $m->logFolder; 5610 $m->releaseRemote(); 5611 5612 return array('path' => $lf, 'logs' => $logs); 5613 5614 } 5615 5616 /** 5617 * Get VM log file contents 5618 * 5619 * @param array $args array of arguments. See function body for details. 5620 * @return string log file contents 5621 */ 5622 public function remote_machineGetLogFile($args) { 5623 5624 // Connect to vboxwebsrv 5625 $this->connect(); 5626 5627 /* @var $m IMachine */ 5628 $m = $this->vbox->findMachine($args['vm']); 5629 $log = ''; 5630 try { 5631 // Read in 8k chunks 5632 while($l = $m->readLog(intval($args['log']),strlen($log),8192)) { 5633 if(!count($l) || !strlen($l[0])) break; 5634 $log .= base64_decode($l[0]); 5635 } 5636 } catch (Exception $null) {} 5637 $m->releaseRemote(); 5638 5639 // Attempt to UTF-8 encode string or json_encode may choke 5640 // and return an empty string 5641 if(function_exists('utf8_encode')) 5642 return utf8_encode($log); 5643 5644 return $log; 5645 } 5646 5647 /** 5648 * Get a list of USB devices attached to a given VM 5649 * 5650 * @param array $args array of arguments. See function body for details. 5651 * @return array list of devices 5652 */ 5653 public function remote_consoleGetUSBDevices($args) { 5654 5655 // Connect to vboxwebsrv 5656 $this->connect(); 5657 5658 /* @var $machine IMachine */ 5659 $machine = $this->vbox->findMachine($args['vm']); 5660 $this->session = $this->websessionManager->getSessionObject($this->vbox->handle); 5661 $machine->lockMachine($this->session->handle, 'Shared'); 5662 5663 $response = array(); 5664 foreach($this->session->console->USBDevices as $u) { /* @var $u IUSBDevice */ 5665 $response[$u->id] = array('id'=>$u->id,'remote'=>$u->remote); 5666 $u->releaseRemote(); 5667 } 5668 5669 $this->session->unlockMachine(); 5670 unset($this->session); 5671 $machine->releaseRemote(); 5672 5673 return $response; 5674 5675 } 5676 5677 /** 5678 * Return a string representing the VirtualBox ExtraData key 5679 * for this port + device + bus type IgnoreFlush setting 5680 * 5681 * @param integer port medium attachment port number 5682 * @param integer device medium attachment device number 5683 * @param string cType controller type 5684 * @return string extra data setting string 5685 */ 5686 private function _util_getIgnoreFlushKey($port,$device,$cType) { 5687 5688 $cTypes = array( 5689 'piix3' => 'piix3ide', 5690 'piix4' => 'piix3ide', 5691 'ich6' => 'piix3ide', 5692 'intelahci' => 'ahci', 5693 'lsilogic' => 'lsilogicscsi', 5694 'buslogic' => 'buslogic', 5695 'lsilogicsas' => 'lsilogicsas' 5696 ); 5697 5698 if(!isset($cTypes[strtolower($cType)])) { 5699 $this->errors[] = new Exception('Invalid controller type: ' . $cType); 5700 return ''; 5701 } 5702 5703 $lun = ((intval($device)*2) + intval($port)); 5704 5705 return str_replace('[b]',$lun,str_replace('[a]',$cTypes[strtolower($cType)],"VBoxInternal/Devices/[a]/0/LUN#[b]/Config/IgnoreFlush")); 5706 5707 } 5708 5709 /** 5710 * Get a newly generated MAC address from VirtualBox 5711 * 5712 * @param array $args array of arguments. See function body for details 5713 * @return string mac address 5714 */ 5715 public function remote_vboxGenerateMacAddress($args) { 5716 5717 // Connect to vboxwebsrv 5718 $this->connect(); 5719 5720 return $this->vbox->host->generateMACAddress(); 5721 5722 } 5723 5724 /** 5725 * Set group definition 5726 * 5727 * @param array $args array of arguments. See function body for details 5728 * @return boolean true on success 5729 */ 5730 public function remote_vboxGroupDefinitionsSet($args) { 5731 5732 $this->connect(); 5733 5734 // Save a list of valid paths 5735 $validGroupPaths = array(); 5736 5737 $groupKey = ($this->settings->phpVboxGroups ? vboxconnector::phpVboxGroupKey : 'GUI/GroupDefinitions'); 5738 5739 // Write out each group definition 5740 foreach($args['groupDefinitions'] as $groupDef) { 5741 5742 $this->vbox->setExtraData($groupKey.$groupDef['path'], $groupDef['order']); 5743 $validGroupPaths[] = $groupDef['path']; 5744 5745 } 5746 5747 // Remove any unused group definitions 5748 $keys = $this->vbox->getExtraDataKeys(); 5749 foreach($keys as $k) { 5750 if(strpos($k,$groupKey) !== 0) continue; 5751 if(array_search(substr($k,strlen($groupKey)), $validGroupPaths) === false) 5752 $this->vbox->setExtraData($k,''); 5753 } 5754 5755 return true; 5756 } 5757 5758 /** 5759 * Return group definitions 5760 * 5761 * @param array $args array of arguments. See function body for details 5762 * @return array group definitions 5763 */ 5764 public function remote_vboxGroupDefinitionsGet($args) { 5765 5766 $this->connect(); 5767 5768 $response = array(); 5769 5770 $keys = $this->vbox->getExtraDataKeys(); 5771 5772 $groupKey = ($this->settings->phpVboxGroups ? vboxconnector::phpVboxGroupKey : 'GUI/GroupDefinitions'); 5773 foreach($keys as $grouppath) { 5774 5775 if(strpos($grouppath,$groupKey) !== 0) continue; 5776 5777 $subgroups = array(); 5778 $machines = array(); 5779 5780 $response[] = array( 5781 'name' => substr($grouppath,strrpos($grouppath,'/')+1), 5782 'path' => substr($grouppath,strlen($groupKey)), 5783 'order' => $this->vbox->getExtraData($grouppath) 5784 ); 5785 } 5786 5787 return $response; 5788 5789 } 5790 5791 /** 5792 * Format a time span in seconds into days / hours / minutes / seconds 5793 * @param integer $t number of seconds 5794 * @return array containing number of days / hours / minutes / seconds 5795 */ 5796 private function _util_splitTime($t) { 5797 5798 $spans = array( 5799 'days' => 86400, 5800 'hours' => 3600, 5801 'minutes' => 60, 5802 'seconds' => 1); 5803 5804 $time = array(); 5805 5806 foreach($spans as $k => $v) { 5807 if(!(floor($t / $v) > 0)) continue; 5808 $time[$k] = floor($t / $v); 5809 $t -= floor($time[$k] * $v); 5810 } 5811 5812 return $time; 5813 } 5814 5815 5816 /** 5817 * Return VBOX result code text for result code 5818 * 5819 * @param integer result code number 5820 * @return string result code text 5821 */ 5822 private function _util_resultCodeText($c) { 5823 5824 $rcodes = new ReflectionClass('VirtualBox_COM_result_codes'); 5825 $rcodes = array_flip($rcodes->getConstants()); 5826 $rcodes['0x80004005'] = 'NS_ERROR_FAILURE'; 5827 5828 return @$rcodes['0x'.strtoupper(dechex($c))] . ' (0x'.strtoupper(dechex($c)).')'; 5829 } 5830} 5831 5832