You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vboxconnector.php 159KB

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