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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828
  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='http://sourceforge.net/p/phpvirtualbox/wiki/Home/' target=_blank>".
  114. "http://sourceforge.net/p/phpvirtualbox/wiki/Home/</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');
  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),$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']);
  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();
  1211. $eprops = array_combine($eprops[1],$eprops[0]);
  1212. $iprops = array_map(create_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']);
  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();
  1599. $eprops = array_combine($eprops[1],$eprops[0]);
  1600. $iprops = array_map(create_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'];
  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. for($i = 0; $i < $host->processorCount; $i++) {
  2587. $response['cpus'][$i] = $host->getProcessorDescription($i);
  2588. }
  2589. /*
  2590. * Supported CPU features?
  2591. */
  2592. $response['cpuFeatures'] = array();
  2593. foreach(array('HWVirtEx'=>'HWVirtEx','PAE'=>'PAE','NestedPaging'=>'Nested Paging','LongMode'=>'Long Mode (64-bit)') as $k=>$v) {
  2594. $response['cpuFeatures'][$v] = $host->getProcessorFeature($k);
  2595. }
  2596. /*
  2597. * NICs
  2598. */
  2599. foreach($host->networkInterfaces as $d) { /* @var $d IHostNetworkInterface */
  2600. $response['networkInterfaces'][] = array(
  2601. 'name' => $d->name,
  2602. 'IPAddress' => $d->IPAddress,
  2603. 'networkMask' => $d->networkMask,
  2604. 'IPV6Supported' => $d->IPV6Supported,
  2605. 'IPV6Address' => $d->IPV6Address,
  2606. 'IPV6NetworkMaskPrefixLength' => $d->IPV6NetworkMaskPrefixLength,
  2607. 'status' => (string)$d->status,
  2608. 'mediumType' => (string)$d->mediumType,
  2609. 'interfaceType' => (string)$d->interfaceType,
  2610. 'hardwareAddress' => $d->hardwareAddress,
  2611. 'networkName' => $d->networkName,
  2612. );
  2613. $d->releaseRemote();
  2614. }
  2615. /*
  2616. * Medium types (DVD and Floppy)
  2617. */
  2618. foreach($host->DVDDrives as $d) { /* @var $d IMedium */
  2619. $response['DVDDrives'][] = array(
  2620. 'id' => $d->id,
  2621. 'name' => $d->name,
  2622. 'location' => $d->location,
  2623. 'description' => $d->description,
  2624. 'deviceType' => 'DVD',
  2625. 'hostDrive' => true
  2626. );
  2627. $d->releaseRemote();
  2628. }
  2629. foreach($host->floppyDrives as $d) { /* @var $d IMedium */
  2630. $response['floppyDrives'][] = array(
  2631. 'id' => $d->id,
  2632. 'name' => $d->name,
  2633. 'location' => $d->location,
  2634. 'description' => $d->description,
  2635. 'deviceType' => 'Floppy',
  2636. 'hostDrive' => true,
  2637. );
  2638. $d->releaseRemote();
  2639. }
  2640. $host->releaseRemote();
  2641. return $response;
  2642. }
  2643. /**
  2644. * Get a list of USB devices attached to the VirtualBox host
  2645. *
  2646. * @param unused $args
  2647. * @return array of USB devices
  2648. */
  2649. public function remote_hostGetUSBDevices($args) {
  2650. // Connect to vboxwebsrv
  2651. $this->connect();
  2652. $response = array();
  2653. foreach($this->vbox->host->USBDevices as $d) { /* @var $d IUSBDevice */
  2654. $response[] = array(
  2655. 'id' => $d->id,
  2656. 'vendorId' => sprintf('%04s',dechex($d->vendorId)),
  2657. 'productId' => sprintf('%04s',dechex($d->productId)),
  2658. 'revision' => sprintf('%04s',dechex($d->revision)),
  2659. 'manufacturer' => $d->manufacturer,
  2660. 'product' => $d->product,
  2661. 'serialNumber' => $d->serialNumber,
  2662. 'address' => $d->address,
  2663. 'port' => $d->port,
  2664. 'version' => $d->version,
  2665. 'portVersion' => $d->portVersion,
  2666. 'remote' => $d->remote,
  2667. 'state' => (string)$d->state,
  2668. );
  2669. $d->releaseRemote();
  2670. }
  2671. return $response;
  2672. }
  2673. /**
  2674. * Get virtual machine or virtualbox host details
  2675. *
  2676. * @param array $args array of arguments. See function body for details.
  2677. * @param ISnapshot $snapshot snapshot instance to use if obtaining snapshot details.
  2678. * @see hostGetDetails()
  2679. * @return array machine details
  2680. */
  2681. public function remote_machineGetDetails($args, $snapshot=null) {
  2682. // Host instead of vm info
  2683. if($args['vm'] == 'host') {
  2684. $response = $this->remote_hostGetDetails($args);
  2685. return $response;
  2686. }
  2687. // Connect to vboxwebsrv
  2688. $this->connect();
  2689. //Get registered machine or snapshot machine
  2690. if($snapshot) {
  2691. /* @var $machine ISnapshot */
  2692. $machine = &$snapshot;
  2693. } else {
  2694. /* @var $machine IMachine */
  2695. $machine = $this->vbox->findMachine($args['vm']);
  2696. // For correct caching, always use id even if a name was passed
  2697. $args['vm'] = $machine->id;
  2698. // Check for accessibility
  2699. if(!$machine->accessible) {
  2700. return array(
  2701. 'name' => $machine->id,
  2702. 'state' => 'Inaccessible',
  2703. 'OSTypeId' => 'Other',
  2704. 'id' => $machine->id,
  2705. 'sessionState' => 'Inaccessible',
  2706. 'accessible' => 0,
  2707. 'accessError' => array(
  2708. 'resultCode' => $this->_util_resultCodeText($machine->accessError->resultCode),
  2709. 'component' => $machine->accessError->component,
  2710. 'text' => $machine->accessError->text)
  2711. );
  2712. }
  2713. }
  2714. // Basic data
  2715. $data = $this->_machineGetDetails($machine);
  2716. // Network Adapters
  2717. $data['networkAdapters'] = $this->_machineGetNetworkAdapters($machine);
  2718. // Storage Controllers
  2719. $data['storageControllers'] = $this->_machineGetStorageControllers($machine);
  2720. // Serial Ports
  2721. $data['serialPorts'] = $this->_machineGetSerialPorts($machine);
  2722. // LPT Ports
  2723. $data['parallelPorts'] = $this->_machineGetParallelPorts($machine);
  2724. // Shared Folders
  2725. $data['sharedFolders'] = $this->_machineGetSharedFolders($machine);
  2726. // USB Controllers
  2727. $data['USBControllers'] = $this->_machineGetUSBControllers($machine);
  2728. $data['USBDeviceFilters'] = $this->_machineGetUSBDeviceFilters($machine);
  2729. if (@$this->settings->enforceVMOwnership )
  2730. {
  2731. $data['name'] = preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $data['name']);
  2732. }
  2733. // Items when not obtaining snapshot machine info
  2734. if(!$snapshot) {
  2735. $data['currentSnapshot'] = ($machine->currentSnapshot->handle ? array('id'=>$machine->currentSnapshot->id,'name'=>$machine->currentSnapshot->name) : null);
  2736. $data['snapshotCount'] = $machine->snapshotCount;
  2737. // Start / stop config
  2738. if(@$this->settings->startStopConfig) {
  2739. $data['startupMode'] = $machine->getExtraData('pvbx/startupMode');
  2740. }
  2741. }
  2742. $machine->releaseRemote();
  2743. $data['accessible'] = 1;
  2744. return $data;
  2745. }
  2746. /**
  2747. * Get runtime data of machine.
  2748. *
  2749. * @param array $args array of arguments. See function body for details.
  2750. * @return array of machine runtime data
  2751. */
  2752. public function remote_machineGetRuntimeData($args) {
  2753. $this->connect();
  2754. /* @var $machine IMachine */
  2755. $machine = $this->vbox->findMachine($args['vm']);
  2756. $data = array(
  2757. 'id' => $args['vm'],
  2758. 'state' => (string)$machine->state
  2759. );
  2760. /*
  2761. * TODO:
  2762. *
  2763. * 5.13.13 getGuestEnteredACPIMode
  2764. boolean IConsole::getGuestEnteredACPIMode()
  2765. Checks if the guest entered the ACPI mode G0 (working) or G1 (sleeping). If this method
  2766. returns false, the guest will most likely not respond to external ACPI events.
  2767. If this method fails, the following error codes may be reported:
  2768.  VBOX_E_INVALID_VM_STATE: Virtual machine not in Running state.
  2769. */
  2770. // Get current console port
  2771. if($data['state'] == 'Running' || $data['state'] == 'Paused') {
  2772. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  2773. $machine->lockMachine($this->session->handle, 'Shared');
  2774. $console = $this->session->console;
  2775. // Get guest additions version
  2776. if(@$this->settings->enableGuestAdditionsVersionDisplay) {
  2777. $data['guestAdditionsVersion'] = $console->guest->additionsVersion;
  2778. }
  2779. $smachine = $this->session->machine;
  2780. $data['CPUExecutionCap'] = $smachine->CPUExecutionCap;
  2781. $data['VRDEServerInfo'] = array('port' => $console->VRDEServerInfo->port);
  2782. $vrde = $smachine->VRDEServer;
  2783. $data['VRDEServer'] = (!$vrde ? null : array(
  2784. 'enabled' => $vrde->enabled,
  2785. 'ports' => $vrde->getVRDEProperty('TCP/Ports'),
  2786. 'netAddress' => $vrde->getVRDEProperty('TCP/Address'),
  2787. 'VNCPassword' => $vrde->getVRDEProperty('VNCPassword'),
  2788. 'authType' => (string)$vrde->authType,
  2789. 'authTimeout' => $vrde->authTimeout,
  2790. 'VRDEExtPack' => (string)$vrde->VRDEExtPack
  2791. ));
  2792. // Get removable media
  2793. $data['storageControllers'] = $this->_machineGetStorageControllers($smachine);
  2794. // Get network adapters
  2795. $data['networkAdapters'] = $this->_machineGetNetworkAdapters($smachine);
  2796. $machine->releaseRemote();
  2797. // Close session and unlock machine
  2798. $this->session->unlockMachine();
  2799. unset($this->session);
  2800. }
  2801. return $data;
  2802. }
  2803. /**
  2804. * Remove a virtual machine
  2805. *
  2806. * @param array $args array of arguments. See function body for details.
  2807. * @return boolean true on success or array of response data
  2808. */
  2809. public function remote_machineRemove($args) {
  2810. // Connect to vboxwebsrv
  2811. $this->connect();
  2812. /* @var $machine IMachine */
  2813. $machine = $this->vbox->findMachine($args['vm']);
  2814. // Only unregister or delete?
  2815. if(!$args['delete']) {
  2816. $machine->unregister('DetachAllReturnNone');
  2817. $machine->releaseRemote();
  2818. } else {
  2819. $hds = array();
  2820. $delete = $machine->unregister('DetachAllReturnHardDisksOnly');
  2821. foreach($delete as $hd) {
  2822. $hds[] = $this->vbox->openMedium($hd->location,'HardDisk')->handle;
  2823. }
  2824. /* @var $progress IProgress */
  2825. $progress = $machine->deleteConfig($hds);
  2826. $machine->releaseRemote();
  2827. // Does an exception exist?
  2828. if($progress) {
  2829. try {
  2830. if($progress->errorInfo->handle) {
  2831. $this->errors[] = new Exception($progress->errorInfo->text);
  2832. $progress->releaseRemote();
  2833. return false;
  2834. }
  2835. } catch (Exception $null) {}
  2836. $this->_util_progressStore($progress);
  2837. return array('progress' => $progress->handle);
  2838. }
  2839. }
  2840. return true;
  2841. }
  2842. /**
  2843. * Create a new Virtual Machine
  2844. *
  2845. * @param array $args array of arguments. See function body for details.
  2846. * @return boolean true on success
  2847. */
  2848. public function remote_machineCreate($args) {
  2849. // Connect to vboxwebsrv
  2850. $this->connect();
  2851. $response = array();
  2852. // quota enforcement
  2853. if ( isset($_SESSION['user']) )
  2854. {
  2855. if ( @isset($this->settings->vmQuotaPerUser) && @$this->settings->vmQuotaPerUser > 0 && !$_SESSION['admin'] )
  2856. {
  2857. $newresp = array('data' => array());
  2858. $vmlist = $this->vboxGetMachines(array(), $newresp);
  2859. if ( count($newresp['data']['vmlist']) >= $this->settings->vmQuotaPerUser )
  2860. {
  2861. // we're over quota!
  2862. // delete the disk we just created
  2863. if ( isset($args['disk']) )
  2864. {
  2865. $this->mediumRemove(array(
  2866. 'id' => $args['disk'],
  2867. 'type' => 'HardDisk',
  2868. 'delete' => true
  2869. ), $newresp);
  2870. }
  2871. throw new Exception("Sorry, you're over quota. You can only create up to {$this->settings->vmQuotaPerUser} VMs.");
  2872. }
  2873. }
  2874. }
  2875. // create machine
  2876. if (@$this->settings->enforceVMOwnership )
  2877. $args['name'] = $_SESSION['user'] . '_' . $args['name'];
  2878. /* Check if file exists */
  2879. $filename = $this->vbox->composeMachineFilename($args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$this->vbox->systemProperties->defaultMachineFolder);
  2880. if($this->remote_fileExists(array('file'=>$filename))) {
  2881. return array('exists' => $filename);
  2882. }
  2883. /* @var $m IMachine */
  2884. $m = $this->vbox->createMachine(null,$args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$args['ostype'],null,null);
  2885. /* Check for phpVirtualBox groups */
  2886. if($this->settings->phpVboxGroups && $args['group']) {
  2887. $m->setExtraData(vboxconnector::phpVboxGroupKey, $args['group']);
  2888. }
  2889. // Set memory
  2890. $m->memorySize = intval($args['memory']);
  2891. // Save and register
  2892. $m->saveSettings();
  2893. $this->vbox->registerMachine($m->handle);
  2894. $vm = $m->id;
  2895. $m->releaseRemote();
  2896. try {
  2897. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  2898. // Lock VM
  2899. /* @var $machine IMachine */
  2900. $machine = $this->vbox->findMachine($vm);
  2901. $machine->lockMachine($this->session->handle,'Write');
  2902. // OS defaults
  2903. $defaults = $this->vbox->getGuestOSType($args['ostype']);
  2904. // Ownership enforcement
  2905. if ( isset($_SESSION['user']) )
  2906. {
  2907. $this->session->machine->setExtraData('phpvb/sso/owner', $_SESSION['user']);
  2908. }
  2909. // set the vboxauthsimple in VM config
  2910. $this->session->machine->setExtraData('VBoxAuthSimple/users/'.$_SESSION['user'].'', $_SESSION['uHash']);
  2911. // Always set
  2912. $this->session->machine->setExtraData('GUI/FirstRun', 'yes');
  2913. try {
  2914. if($this->session->machine->VRDEServer && $this->vbox->systemProperties->defaultVRDEExtPack) {
  2915. $this->session->machine->VRDEServer->enabled = 1;
  2916. $this->session->machine->VRDEServer->authTimeout = 5000;
  2917. $this->session->machine->VRDEServer->setVRDEProperty('TCP/Ports',($this->settings->vrdeports ? $this->settings->vrdeports : '3390-5000'));
  2918. }
  2919. } catch (Exception $e) {
  2920. //Ignore
  2921. }
  2922. // Other defaults
  2923. $this->session->machine->BIOSSettings->IOAPICEnabled = $defaults->recommendedIOAPIC;
  2924. $this->session->machine->RTCUseUTC = $defaults->recommendedRTCUseUTC;
  2925. $this->session->machine->firmwareType = (string)$defaults->recommendedFirmware;
  2926. $this->session->machine->chipsetType = (string)$defaults->recommendedChipset;
  2927. if(intval($defaults->recommendedVRAM) > 0) $this->session->machine->VRAMSize = intval($defaults->recommendedVRAM);
  2928. $this->session->machine->setCpuProperty('PAE',$defaults->recommendedPAE);
  2929. // USB input devices
  2930. if($defaults->recommendedUSBHid) {
  2931. $this->session->machine->pointingHIDType = 'USBMouse';
  2932. $this->session->machine->keyboardHIDType = 'USBKeyboard';
  2933. }
  2934. /* Only if acceleration configuration is available */
  2935. if($this->vbox->host->getProcessorFeature('HWVirtEx')) {
  2936. $this->session->machine->setHWVirtExProperty('Enabled',$defaults->recommendedVirtEx);
  2937. }
  2938. /*
  2939. * Hard Disk and DVD/CD Drive
  2940. */
  2941. $DVDbusType = (string)$defaults->recommendedDVDStorageBus;
  2942. $DVDconType = (string)$defaults->recommendedDVDStorageController;
  2943. // Attach harddisk?
  2944. if($args['disk']) {
  2945. $HDbusType = (string)$defaults->recommendedHDStorageBus;
  2946. $HDconType = (string)$defaults->recommendedHDStorageController;
  2947. $bus = new StorageBus(null,$HDbusType);
  2948. $sc = $this->session->machine->addStorageController(trans($HDbusType,'UIMachineSettingsStorage'),(string)$bus);
  2949. $sc->controllerType = $HDconType;
  2950. $sc->useHostIOCache = (bool)$this->vbox->systemProperties->getDefaultIoCacheSettingForStorageController($HDconType);
  2951. // Set port count?
  2952. if($HDbusType == 'SATA') {
  2953. $sc->portCount = (($HDbusType == $DVDbusType) ? 2 : 1);
  2954. }
  2955. $sc->releaseRemote();
  2956. $m = $this->vbox->openMedium($args['disk'],'HardDisk');
  2957. $this->session->machine->attachDevice(trans($HDbusType,'UIMachineSettingsStorage'),0,0,'HardDisk',$m->handle);
  2958. $m->releaseRemote();
  2959. }
  2960. // Attach DVD/CDROM
  2961. if($DVDbusType) {
  2962. if(!$args['disk'] || ($HDbusType != $DVDbusType)) {
  2963. $bus = new StorageBus(null,$DVDbusType);
  2964. $sc = $this->session->machine->addStorageController(trans($DVDbusType,'UIMachineSettingsStorage'),(string)$bus);
  2965. $sc->controllerType = $DVDconType;
  2966. $sc->useHostIOCache = (bool)$this->vbox->systemProperties->getDefaultIoCacheSettingForStorageController($DVDconType);
  2967. // Set port count?
  2968. if($DVDbusType == 'SATA') {
  2969. $sc->portCount = ($args['disk'] ? 1 : 2);
  2970. }
  2971. $sc->releaseRemote();
  2972. }
  2973. $this->session->machine->attachDevice(trans($DVDbusType,'UIMachineSettingsStorage'),1,0,'DVD',null);
  2974. }
  2975. $this->session->machine->saveSettings();
  2976. $this->session->unlockMachine();
  2977. $this->session = null;
  2978. $machine->releaseRemote();
  2979. } catch (Exception $e) {
  2980. $this->errors[] = $e;
  2981. return false;
  2982. }
  2983. return true;
  2984. }
  2985. /**
  2986. * Return a list of network adapters attached to machine $m
  2987. *
  2988. * @param IMachine $m virtual machine instance
  2989. * @param int $slot optional slot of single network adapter to get
  2990. * @return array of network adapter information
  2991. */
  2992. private function _machineGetNetworkAdapters(&$m, $slot=false) {
  2993. $adapters = array();
  2994. for($i = ($slot === false ? 0 : $slot); $i < ($slot === false ? $this->settings->nicMax : ($slot+1)); $i++) {
  2995. /* @var $n INetworkAdapter */
  2996. $n = $m->getNetworkAdapter($i);
  2997. // Avoid duplicate calls
  2998. $at = (string)$n->attachmentType;
  2999. if($at == 'NAT') $nd = $n->NATEngine; /* @var $nd INATEngine */
  3000. else $nd = null;
  3001. $props = $n->getProperties();
  3002. $props = implode("\n",array_map(create_function('$a,$b','return "$a=$b";'),$props[1],$props[0]));
  3003. $adapters[] = array(
  3004. 'adapterType' => (string)$n->adapterType,
  3005. 'slot' => $n->slot,
  3006. 'enabled' => $n->enabled,
  3007. 'MACAddress' => $n->MACAddress,
  3008. 'attachmentType' => $at,
  3009. 'genericDriver' => $n->genericDriver,
  3010. 'hostOnlyInterface' => $n->hostOnlyInterface,
  3011. 'bridgedInterface' => $n->bridgedInterface,
  3012. 'properties' => $props,
  3013. 'internalNetwork' => $n->internalNetwork,
  3014. 'NATNetwork' => $n->NATNetwork,
  3015. 'promiscModePolicy' => (string)$n->promiscModePolicy,
  3016. 'VDENetwork' => ($this->settings->enableVDE ? $n->VDENetwork : ''),
  3017. 'cableConnected' => $n->cableConnected,
  3018. 'NATEngine' => ($at == 'NAT' ?
  3019. array('aliasMode' => intval($nd->aliasMode),'DNSPassDomain' => $nd->DNSPassDomain, 'DNSProxy' => $nd->DNSProxy, 'DNSUseHostResolver' => $nd->DNSUseHostResolver, 'hostIP' => $nd->hostIP)
  3020. : array('aliasMode' => 0,'DNSPassDomain' => 0, 'DNSProxy' => 0, 'DNSUseHostResolver' => 0, 'hostIP' => '')),
  3021. 'lineSpeed' => $n->lineSpeed,
  3022. 'redirects' => (
  3023. $at == 'NAT' ?
  3024. $nd->getRedirects()
  3025. : array()
  3026. )
  3027. );
  3028. $n->releaseRemote();
  3029. }
  3030. return $adapters;
  3031. }
  3032. /**
  3033. * Return a list of virtual machines along with their states and other basic info
  3034. *
  3035. * @param array $args array of arguments. See function body for details.
  3036. * @return array list of machines
  3037. */
  3038. public function remote_vboxGetMachines($args) {
  3039. // Connect to vboxwebsrv
  3040. $this->connect();
  3041. $vmlist = array();
  3042. // Look for a request for a single vm
  3043. if($args['vm']) {
  3044. $machines = array($this->vbox->findMachine($args['vm']));
  3045. // Full list
  3046. } else {
  3047. //Get a list of registered machines
  3048. $machines = $this->vbox->machines;
  3049. }
  3050. foreach ($machines as $machine) { /* @var $machine IMachine */
  3051. try {
  3052. if(!$machine->accessible) {
  3053. $vmlist[] = array(
  3054. 'name' => $machine->id,
  3055. 'state' => 'Inaccessible',
  3056. 'OSTypeId' => 'Other',
  3057. 'id' => $machine->id,
  3058. 'sessionState' => 'Inaccessible',
  3059. 'accessible' => 0,
  3060. 'accessError' => array(
  3061. 'resultCode' => $this->_util_resultCodeText($machine->accessError->resultCode),
  3062. 'component' => $machine->accessError->component,
  3063. 'text' => $machine->accessError->text),
  3064. 'lastStateChange' => 0,
  3065. 'groups' => array(),
  3066. 'currentSnapshot' => ''
  3067. );
  3068. continue;
  3069. }
  3070. if($this->settings->phpVboxGroups) {
  3071. $groups = explode(',',$machine->getExtraData(vboxconnector::phpVboxGroupKey));
  3072. if(!is_array($groups) || (count($groups) == 1 && !$groups[0])) $groups = array("/");
  3073. } else {
  3074. $groups = $machine->groups;
  3075. }
  3076. usort($groups, 'strnatcasecmp');
  3077. $vmlist[] = array(
  3078. 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $machine->name) : $machine->name,
  3079. 'state' => (string)$machine->state,
  3080. 'OSTypeId' => $machine->getOSTypeId(),
  3081. 'owner' => (@$this->settings->enforceVMOwnership ? $machine->getExtraData("phpvb/sso/owner") : ''),
  3082. 'groups' => $groups,
  3083. 'lastStateChange' => (string)($machine->lastStateChange/1000),
  3084. 'id' => $machine->id,
  3085. 'currentStateModified' => $machine->currentStateModified,
  3086. 'sessionState' => (string)$machine->sessionState,
  3087. 'currentSnapshotName' => ($machine->currentSnapshot->handle ? $machine->currentSnapshot->name : ''),
  3088. 'customIcon' => (@$this->settings->enableCustomIcons ? $machine->getExtraData('phpvb/icon') : '')
  3089. );
  3090. if($machine->currentSnapshot->handle) $machine->currentSnapshot->releaseRemote();
  3091. } catch (Exception $e) {
  3092. if($machine) {
  3093. $vmlist[] = array(
  3094. 'name' => $machine->id,
  3095. 'state' => 'Inaccessible',
  3096. 'OSTypeId' => 'Other',
  3097. 'id' => $machine->id,
  3098. 'sessionState' => 'Inaccessible',
  3099. 'lastStateChange' => 0,
  3100. 'groups' => array(),
  3101. 'currentSnapshot' => ''
  3102. );
  3103. } else {
  3104. $this->errors[] = $e;
  3105. }
  3106. }
  3107. try {
  3108. $machine->releaseRemote();
  3109. } catch (Exception $e) { }
  3110. }
  3111. return $vmlist;
  3112. }
  3113. /**
  3114. * Creates a new exception so that input can be debugged.
  3115. *
  3116. * @param array $args array of arguments. See function body for details.
  3117. * @return boolean true on success
  3118. */
  3119. public function debugInput($args) {
  3120. $this->errors[] = new Exception('debug');
  3121. return true;
  3122. }
  3123. /**
  3124. * Get a list of media registered with VirtualBox
  3125. *
  3126. * @param unused $args
  3127. * @param array $response response data passed byref populated by the function
  3128. * @return array of media
  3129. */
  3130. public function remote_vboxGetMedia($args) {
  3131. // Connect to vboxwebsrv
  3132. $this->connect();
  3133. $response = array();
  3134. $mds = array($this->vbox->hardDisks,$this->vbox->DVDImages,$this->vbox->floppyImages);
  3135. for($i=0;$i<3;$i++) {
  3136. foreach($mds[$i] as $m) {
  3137. /* @var $m IMedium */
  3138. $response[] = $this->_mediumGetDetails($m);
  3139. $m->releaseRemote();
  3140. }
  3141. }
  3142. return $response;
  3143. }
  3144. /**
  3145. * Get USB controller information
  3146. *
  3147. * @param IMachine $m virtual machine instance
  3148. * @return array USB controller info
  3149. */
  3150. private function _machineGetUSBControllers(&$m) {
  3151. /* @var $u IUSBController */
  3152. $controllers = &$m->USBControllers;
  3153. $rcons = array();
  3154. foreach($controllers as $c) {
  3155. $rcons[] = array(
  3156. 'name' => $c->name,
  3157. 'type' => (string)$c->type
  3158. );
  3159. $c->releaseRemote();
  3160. }
  3161. return $rcons;
  3162. }
  3163. /**
  3164. * Get USB device filters
  3165. *
  3166. * @param IMachine $m virtual machine instance
  3167. * @return array USB device filters
  3168. */
  3169. private function _machineGetUSBDeviceFilters(&$m) {
  3170. $deviceFilters = array();
  3171. foreach($m->USBDeviceFilters->deviceFilters as $df) { /* @var $df IUSBDeviceFilter */
  3172. $deviceFilters[] = array(
  3173. 'name' => $df->name,
  3174. 'active' => $df->active,
  3175. 'vendorId' => $df->vendorId,
  3176. 'productId' => $df->productId,
  3177. 'revision' => $df->revision,
  3178. 'manufacturer' => $df->manufacturer,
  3179. 'product' => $df->product,
  3180. 'serialNumber' => $df->serialNumber,
  3181. 'port' => $df->port,
  3182. 'remote' => $df->remote
  3183. );
  3184. $df->releaseRemote();
  3185. }
  3186. return $deviceFilters;
  3187. }
  3188. /**
  3189. * Return top-level virtual machine or snapshot information
  3190. *
  3191. * @param IMachine $m virtual machine instance
  3192. * @return array vm or snapshot data
  3193. */
  3194. private function _machineGetDetails(&$m) {
  3195. if($this->settings->phpVboxGroups) {
  3196. $groups = explode(',',$m->getExtraData(vboxconnector::phpVboxGroupKey));
  3197. if(!is_array($groups) || (count($groups) == 1 && !$groups[0])) $groups = array("/");
  3198. } else {
  3199. $groups = $m->groups;
  3200. }
  3201. usort($groups, 'strnatcasecmp');
  3202. return array(
  3203. 'name' => @$this->settings->enforceVMOwnership ? preg_replace('/^' . preg_quote($_SESSION['user']) . '_/', '', $m->name) : $m->name,
  3204. 'description' => $m->description,
  3205. 'groups' => $groups,
  3206. 'id' => $m->id,
  3207. 'autostopType' => ($this->settings->vboxAutostartConfig ? (string)$m->autostopType : ''),
  3208. 'autostartEnabled' => ($this->settings->vboxAutostartConfig && $m->autostartEnabled),
  3209. 'autostartDelay' => ($this->settings->vboxAutostartConfig ? intval($m->autostartDelay) : '0'),
  3210. 'settingsFilePath' => $m->settingsFilePath,
  3211. 'paravirtProvider' => (string)$m->paravirtProvider,
  3212. 'OSTypeId' => $m->OSTypeId,
  3213. 'OSTypeDesc' => $this->vbox->getGuestOSType($m->OSTypeId)->description,
  3214. 'CPUCount' => $m->CPUCount,
  3215. 'HPETEnabled' => $m->HPETEnabled,
  3216. 'memorySize' => $m->memorySize,
  3217. 'VRAMSize' => $m->VRAMSize,
  3218. 'pointingHIDType' => (string)$m->pointingHIDType,
  3219. 'keyboardHIDType' => (string)$m->keyboardHIDType,
  3220. 'accelerate3DEnabled' => $m->accelerate3DEnabled,
  3221. 'accelerate2DVideoEnabled' => $m->accelerate2DVideoEnabled,
  3222. 'BIOSSettings' => array(
  3223. 'ACPIEnabled' => $m->BIOSSettings->ACPIEnabled,
  3224. 'IOAPICEnabled' => $m->BIOSSettings->IOAPICEnabled,
  3225. 'timeOffset' => $m->BIOSSettings->timeOffset
  3226. ),
  3227. 'firmwareType' => (string)$m->firmwareType,
  3228. 'snapshotFolder' => $m->snapshotFolder,
  3229. 'monitorCount' => $m->monitorCount,
  3230. 'pageFusionEnabled' => $m->pageFusionEnabled,
  3231. 'VRDEServer' => (!$m->VRDEServer ? null : array(
  3232. 'enabled' => $m->VRDEServer->enabled,
  3233. 'ports' => $m->VRDEServer->getVRDEProperty('TCP/Ports'),
  3234. 'netAddress' => $m->VRDEServer->getVRDEProperty('TCP/Address'),
  3235. 'VNCPassword' => $m->VRDEServer->getVRDEProperty('VNCPassword'),
  3236. 'authType' => (string)$m->VRDEServer->authType,
  3237. 'authTimeout' => $m->VRDEServer->authTimeout,
  3238. 'allowMultiConnection' => $m->VRDEServer->allowMultiConnection,
  3239. 'VRDEExtPack' => (string)$m->VRDEServer->VRDEExtPack
  3240. )),
  3241. 'audioAdapter' => array(
  3242. 'enabled' => $m->audioAdapter->enabled,
  3243. 'audioController' => (string)$m->audioAdapter->audioController,
  3244. 'audioDriver' => (string)$m->audioAdapter->audioDriver,
  3245. ),
  3246. 'RTCUseUTC' => $m->RTCUseUTC,
  3247. 'EffectiveParavirtProvider' => (string)$m->getEffectiveParavirtProvider(),
  3248. 'HWVirtExProperties' => array(
  3249. 'Enabled' => $m->getHWVirtExProperty('Enabled'),
  3250. 'NestedPaging' => $m->getHWVirtExProperty('NestedPaging'),
  3251. 'LargePages' => $m->getHWVirtExProperty('LargePages'),
  3252. 'UnrestrictedExecution' => $m->getHWVirtExProperty('UnrestrictedExecution'),
  3253. 'VPID' => $m->getHWVirtExProperty('VPID')
  3254. ),
  3255. 'CpuProperties' => array(
  3256. 'PAE' => $m->getCpuProperty('PAE')
  3257. ),
  3258. 'bootOrder' => $this->_machineGetBootOrder($m),
  3259. 'chipsetType' => (string)$m->chipsetType,
  3260. 'GUI' => array(
  3261. 'FirstRun' => $m->getExtraData('GUI/FirstRun'),
  3262. ),
  3263. 'customIcon' => (@$this->settings->enableCustomIcons ? $m->getExtraData('phpvb/icon') : ''),
  3264. 'disableHostTimeSync' => intval($m->getExtraData("VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled")),
  3265. 'CPUExecutionCap' => $m->CPUExecutionCap
  3266. );
  3267. }
  3268. /**
  3269. * Get virtual machine boot order
  3270. *
  3271. * @param IMachine $m virtual machine instance
  3272. * @return array boot order
  3273. */
  3274. private function _machineGetBootOrder(&$m) {
  3275. $return = array();
  3276. $mbp = $this->vbox->systemProperties->maxBootPosition;
  3277. for($i = 0; $i < $mbp; $i ++) {
  3278. if(($b = (string)$m->getBootOrder($i + 1)) == 'Null') continue;
  3279. $return[] = $b;
  3280. }
  3281. return $return;
  3282. }
  3283. /**
  3284. * Get serial port configuration for a virtual machine or snapshot
  3285. *
  3286. * @param IMachine $m virtual machine instance
  3287. * @return array serial port info
  3288. */
  3289. private function _machineGetSerialPorts(&$m) {
  3290. $ports = array();
  3291. $max = $this->vbox->systemProperties->serialPortCount;
  3292. for($i = 0; $i < $max; $i++) {
  3293. try {
  3294. /* @var $p ISerialPort */
  3295. $p = $m->getSerialPort($i);
  3296. $ports[] = array(
  3297. 'slot' => $p->slot,
  3298. 'enabled' => $p->enabled,
  3299. 'IOBase' => '0x'.strtoupper(sprintf('%3s',dechex($p->IOBase))),
  3300. 'IRQ' => $p->IRQ,
  3301. 'hostMode' => (string)$p->hostMode,
  3302. 'server' => $p->server,
  3303. 'path' => $p->path
  3304. );
  3305. $p->releaseRemote();
  3306. } catch (Exception $e) {
  3307. // Ignore
  3308. }
  3309. }
  3310. return $ports;
  3311. }
  3312. /**
  3313. * Get parallel port configuration for a virtual machine or snapshot
  3314. *
  3315. * @param IMachine $m virtual machine instance
  3316. * @return array parallel port info
  3317. */
  3318. private function _machineGetParallelPorts(&$m) {
  3319. if(!@$this->settings->enableLPTConfig) return array();
  3320. $ports = array();
  3321. $max = $this->vbox->systemProperties->parallelPortCount;
  3322. for($i = 0; $i < $max; $i++) {
  3323. try {
  3324. /* @var $p IParallelPort */
  3325. $p = $m->getParallelPort($i);
  3326. $ports[] = array(
  3327. 'slot' => $p->slot,
  3328. 'enabled' => $p->enabled,
  3329. 'IOBase' => '0x'.strtoupper(sprintf('%3s',dechex($p->IOBase))),
  3330. 'IRQ' => $p->IRQ,
  3331. 'path' => $p->path
  3332. );
  3333. $p->releaseRemote();
  3334. } catch (Exception $e) {
  3335. // Ignore
  3336. }
  3337. }
  3338. return $ports;
  3339. }
  3340. /**
  3341. * Get shared folder configuration for a virtual machine or snapshot
  3342. *
  3343. * @param IMachine $m virtual machine instance
  3344. * @return array shared folder info
  3345. */
  3346. private function _machineGetSharedFolders(&$m) {
  3347. $sfs = &$m->sharedFolders;
  3348. $return = array();
  3349. foreach($sfs as $sf) { /* @var $sf ISharedFolder */
  3350. $return[] = array(
  3351. 'name' => $sf->name,
  3352. 'hostPath' => $sf->hostPath,
  3353. 'accessible' => $sf->accessible,
  3354. 'writable' => $sf->writable,
  3355. 'autoMount' => $sf->autoMount,
  3356. 'lastAccessError' => $sf->lastAccessError,
  3357. 'type' => 'machine'
  3358. );
  3359. }
  3360. return $return;
  3361. }
  3362. /**
  3363. * Add encryption password to VM console
  3364. *
  3365. * @param array $args array of arguments. See function body for details.
  3366. * @return true on success
  3367. */
  3368. public function remote_consoleAddDiskEncryptionPasswords($args) {
  3369. $this->connect();
  3370. /* @var $machine IMachine */
  3371. $machine = $this->vbox->findMachine($args['vm']);
  3372. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3373. $machine->lockMachine($this->session->handle,'Shared');
  3374. $response = array('accepted'=>array(),'failed'=>array(),'errors'=>array());
  3375. foreach($args['passwords'] as $creds) {
  3376. try {
  3377. $this->session->console->removeDiskEncryptionPassword($creds['id']);
  3378. } catch(Exception $e) {
  3379. // It may not exist yet
  3380. }
  3381. try {
  3382. $this->session->console->addDiskEncryptionPassword($creds['id'], $creds['password'], (bool)@$args['clearOnSuspend']);
  3383. $response['accepted'][] = $creds['id'];
  3384. } catch (Exception $e) {
  3385. $response['failed'][] = $creds['id'];
  3386. $response['errors'][] = $e->getMessage();
  3387. }
  3388. }
  3389. $this->session->unlockMachine();
  3390. unset($this->session);
  3391. $machine->releaseRemote();
  3392. return $response;
  3393. }
  3394. /**
  3395. * Get a list of transient (temporary) shared folders
  3396. *
  3397. * @param array $args array of arguments. See function body for details.
  3398. * @return array of shared folders
  3399. */
  3400. public function remote_consoleGetSharedFolders($args) {
  3401. $this->connect();
  3402. /* @var $machine IMachine */
  3403. $machine = $this->vbox->findMachine($args['vm']);
  3404. // No need to continue if machine is not running
  3405. if((string)$machine->state != 'Running') {
  3406. $machine->releaseRemote();
  3407. return true;
  3408. }
  3409. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3410. $machine->lockMachine($this->session->handle,'Shared');
  3411. $sfs = $this->session->console->sharedFolders;
  3412. $response = array();
  3413. foreach($sfs as $sf) { /* @var $sf ISharedFolder */
  3414. $response[] = array(
  3415. 'name' => $sf->name,
  3416. 'hostPath' => $sf->hostPath,
  3417. 'accessible' => $sf->accessible,
  3418. 'writable' => $sf->writable,
  3419. 'autoMount' => $sf->autoMount,
  3420. 'lastAccessError' => $sf->lastAccessError,
  3421. 'type' => 'transient'
  3422. );
  3423. }
  3424. $this->session->unlockMachine();
  3425. unset($this->session);
  3426. $machine->releaseRemote();
  3427. return $response;
  3428. }
  3429. /**
  3430. * Get VirtualBox Host OS specific directory separator
  3431. *
  3432. * @return string directory separator string
  3433. */
  3434. public function getDsep() {
  3435. if(!$this->dsep) {
  3436. /* No need to go through vbox if local browser is true */
  3437. if($this->settings->browserLocal) {
  3438. $this->dsep = DIRECTORY_SEPARATOR;
  3439. } else {
  3440. $this->connect();
  3441. if(stripos($this->vbox->host->operatingSystem,'windows') !== false) {
  3442. $this->dsep = '\\';
  3443. } else {
  3444. $this->dsep = '/';
  3445. }
  3446. }
  3447. }
  3448. return $this->dsep;
  3449. }
  3450. /**
  3451. * Get medium attachment information for all medium attachments in $mas
  3452. *
  3453. * @param IMediumAttachment[] $mas list of IMediumAttachment instances
  3454. * @return array medium attachment info
  3455. */
  3456. private function _machineGetMediumAttachments(&$mas) {
  3457. $return = array();
  3458. foreach($mas as $ma) { /** @var $ma IMediumAttachment */
  3459. $return[] = array(
  3460. 'medium' => ($ma->medium->handle ? array('id'=>$ma->medium->id) : null),
  3461. 'controller' => $ma->controller,
  3462. 'port' => $ma->port,
  3463. 'device' => $ma->device,
  3464. 'type' => (string)$ma->type,
  3465. 'passthrough' => $ma->passthrough,
  3466. 'temporaryEject' => $ma->temporaryEject,
  3467. 'nonRotational' => $ma->nonRotational,
  3468. 'hotPluggable' => $ma->hotPluggable,
  3469. );
  3470. }
  3471. // sort by port then device
  3472. usort($return,create_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;'));
  3473. return $return;
  3474. }
  3475. /**
  3476. * Save snapshot details ( description or name)
  3477. *
  3478. * @param array $args array of arguments. See function body for details.
  3479. * @return boolean true on success
  3480. */
  3481. public function remote_snapshotSave($args) {
  3482. // Connect to vboxwebsrv
  3483. $this->connect();
  3484. /* @var $vm IMachine */
  3485. $vm = $this->vbox->findMachine($args['vm']);
  3486. /* @var $snapshot ISnapshot */
  3487. $snapshot = $vm->findSnapshot($args['snapshot']);
  3488. $snapshot->name = $args['name'];
  3489. $snapshot->description = $args['description'];
  3490. // cleanup
  3491. $snapshot->releaseRemote();
  3492. $vm->releaseRemote();
  3493. return true;
  3494. }
  3495. /**
  3496. * Get snapshot details
  3497. *
  3498. * @param array $args array of arguments. See function body for details.
  3499. * @return array containing snapshot details
  3500. */
  3501. public function remote_snapshotGetDetails($args) {
  3502. // Connect to vboxwebsrv
  3503. $this->connect();
  3504. /* @var $vm IMachine */
  3505. $vm = $this->vbox->findMachine($args['vm']);
  3506. /* @var $snapshot ISnapshot */
  3507. $snapshot = $vm->findSnapshot($args['snapshot']);
  3508. $response = $this->_snapshotGetDetails($snapshot,false);
  3509. $response['machine'] = $this->remote_machineGetDetails(array(),$snapshot->machine);
  3510. // cleanup
  3511. $snapshot->releaseRemote();
  3512. $vm->releaseRemote();
  3513. return $response;
  3514. }
  3515. /**
  3516. * Restore a snapshot
  3517. *
  3518. * @param array $args array of arguments. See function body for details.
  3519. * @return array response data containing progress operation id
  3520. */
  3521. public function remote_snapshotRestore($args) {
  3522. // Connect to vboxwebsrv
  3523. $this->connect();
  3524. $progress = $this->session = null;
  3525. try {
  3526. // Open session to machine
  3527. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3528. /* @var $machine IMachine */
  3529. $machine = $this->vbox->findMachine($args['vm']);
  3530. $machine->lockMachine($this->session->handle, 'Write');
  3531. /* @var $snapshot ISnapshot */
  3532. $snapshot = $this->session->machine->findSnapshot($args['snapshot']);
  3533. /* @var $progress IProgress */
  3534. $progress = $this->session->machine->restoreSnapshot($snapshot->handle);
  3535. $snapshot->releaseRemote();
  3536. $machine->releaseRemote();
  3537. // Does an exception exist?
  3538. try {
  3539. if($progress->errorInfo->handle) {
  3540. $this->errors[] = new Exception($progress->errorInfo->text);
  3541. $progress->releaseRemote();
  3542. return false;
  3543. }
  3544. } catch (Exception $null) {}
  3545. $this->_util_progressStore($progress);
  3546. } catch (Exception $e) {
  3547. $this->errors[] = $e;
  3548. if($this->session->handle) {
  3549. try{$this->session->unlockMachine();}catch(Exception $e){}
  3550. }
  3551. return false;
  3552. }
  3553. return array('progress' => $progress->handle);
  3554. }
  3555. /**
  3556. * Delete a snapshot
  3557. *
  3558. * @param array $args array of arguments. See function body for details.
  3559. * @return array response data containing progress operation id
  3560. */
  3561. public function remote_snapshotDelete($args) {
  3562. // Connect to vboxwebsrv
  3563. $this->connect();
  3564. $progress = $this->session = null;
  3565. try {
  3566. // Open session to machine
  3567. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3568. /* @var $machine IMachine */
  3569. $machine = $this->vbox->findMachine($args['vm']);
  3570. $machine->lockMachine($this->session->handle, 'Shared');
  3571. /* @var $progress IProgress */
  3572. $progress = $this->session->machine->deleteSnapshot($args['snapshot']);
  3573. $machine->releaseRemote();
  3574. // Does an exception exist?
  3575. try {
  3576. if($progress->errorInfo->handle) {
  3577. $this->errors[] = new Exception($progress->errorInfo->text);
  3578. $progress->releaseRemote();
  3579. return false;
  3580. }
  3581. } catch (Exception $null) {}
  3582. $this->_util_progressStore($progress);
  3583. } catch (Exception $e) {
  3584. $this->errors[] = $e;
  3585. if($this->session->handle) {
  3586. try{$this->session->unlockMachine();$this->session=null;}catch(Exception $e){}
  3587. }
  3588. return false;
  3589. }
  3590. return array('progress' => $progress->handle);
  3591. }
  3592. /**
  3593. * Take a snapshot
  3594. *
  3595. * @param array $args array of arguments. See function body for details.
  3596. * @return array response data containing progress operation id
  3597. */
  3598. public function remote_snapshotTake($args) {
  3599. // Connect to vboxwebsrv
  3600. $this->connect();
  3601. /* @var $machine IMachine */
  3602. $machine = $this->vbox->findMachine($args['vm']);
  3603. $progress = $this->session = null;
  3604. try {
  3605. // Open session to machine
  3606. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3607. $machine->lockMachine($this->session->handle, ((string)$machine->sessionState == 'Unlocked' ? 'Write' : 'Shared'));
  3608. /* @var $progress IProgress */
  3609. list($progress, $snapshotId) = $this->session->machine->takeSnapshot($args['name'], $args['description']);
  3610. // Does an exception exist?
  3611. try {
  3612. if($progress->errorInfo->handle) {
  3613. $this->errors[] = new Exception($progress->errorInfo->text);
  3614. $progress->releaseRemote();
  3615. try{$this->session->unlockMachine(); $this->session=null;}catch(Exception $ed){}
  3616. return false;
  3617. }
  3618. } catch (Exception $null) {}
  3619. $this->_util_progressStore($progress);
  3620. } catch (Exception $e) {
  3621. if(!$progress->handle && $this->session->handle) {
  3622. try{$this->session->unlockMachine();$this->session=null;}catch(Exception $e){}
  3623. }
  3624. return false;
  3625. }
  3626. return array('progress' => $progress->handle);
  3627. }
  3628. /**
  3629. * Get a list of snapshots for a machine
  3630. *
  3631. * @param array $args array of arguments. See function body for details.
  3632. * @return array list of snapshots
  3633. */
  3634. public function remote_machineGetSnapshots($args) {
  3635. // Connect to vboxwebsrv
  3636. $this->connect();
  3637. /* @var $machine IMachine */
  3638. $machine = $this->vbox->findMachine($args['vm']);
  3639. $response = array('vm' => $args['vm'],
  3640. 'snapshot' => array(),
  3641. 'currentSnapshotId' => null);
  3642. /* No snapshots? Empty array */
  3643. if($machine->snapshotCount < 1) {
  3644. return $response;
  3645. } else {
  3646. /* @var $s ISnapshot */
  3647. $s = $machine->findSnapshot(null);
  3648. $response['snapshot'] = $this->_snapshotGetDetails($s,true);
  3649. $s->releaseRemote();
  3650. }
  3651. $response['currentSnapshotId'] = ($machine->currentSnapshot->handle ? $machine->currentSnapshot->id : '');
  3652. if($machine->currentSnapshot->handle) $machine->currentSnapshot->releaseRemote();
  3653. $machine->releaseRemote();
  3654. return $response;
  3655. }
  3656. /**
  3657. * Return details about snapshot $s
  3658. *
  3659. * @param ISnapshot $s snapshot instance
  3660. * @param boolean $sninfo traverse child snapshots
  3661. * @return array snapshot info
  3662. */
  3663. private function _snapshotGetDetails(&$s,$sninfo=false) {
  3664. $children = array();
  3665. if($sninfo)
  3666. foreach($s->children as $c) { /* @var $c ISnapshot */
  3667. $children[] = $this->_snapshotGetDetails($c, true);
  3668. $c->releaseRemote();
  3669. }
  3670. // Avoid multiple soap calls
  3671. $timestamp = (string)$s->timeStamp;
  3672. return array(
  3673. 'id' => $s->id,
  3674. 'name' => $s->name,
  3675. 'description' => $s->description,
  3676. 'timeStamp' => floor($timestamp/1000),
  3677. 'timeStampSplit' => $this->_util_splitTime(time() - floor($timestamp/1000)),
  3678. 'online' => $s->online
  3679. ) + (
  3680. ($sninfo ? array('children' => $children) : array())
  3681. );
  3682. }
  3683. /**
  3684. * Return details about storage controllers for machine $m
  3685. *
  3686. * @param IMachine $m virtual machine instance
  3687. * @return array storage controllers' details
  3688. */
  3689. private function _machineGetStorageControllers(&$m) {
  3690. $sc = array();
  3691. $scs = $m->storageControllers;
  3692. foreach($scs as $c) { /* @var $c IStorageController */
  3693. $sc[] = array(
  3694. 'name' => $c->name,
  3695. 'maxDevicesPerPortCount' => $c->maxDevicesPerPortCount,
  3696. 'useHostIOCache' => $c->useHostIOCache,
  3697. 'minPortCount' => $c->minPortCount,
  3698. 'maxPortCount' => $c->maxPortCount,
  3699. 'portCount' => $c->portCount,
  3700. 'bus' => (string)$c->bus,
  3701. 'controllerType' => (string)$c->controllerType,
  3702. 'mediumAttachments' => $this->_machineGetMediumAttachments($m->getMediumAttachmentsOfController($c->name), $m->id)
  3703. );
  3704. $c->releaseRemote();
  3705. }
  3706. for($i = 0; $i < count($sc); $i++) {
  3707. for($a = 0; $a < count($sc[$i]['mediumAttachments']); $a++) {
  3708. // Value of '' means it is not applicable
  3709. $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = '';
  3710. // Only valid for HardDisks
  3711. if($sc[$i]['mediumAttachments'][$a]['type'] != 'HardDisk') continue;
  3712. // Get appropriate key
  3713. $xtra = $this->_util_getIgnoreFlushKey($sc[$i]['mediumAttachments'][$a]['port'], $sc[$i]['mediumAttachments'][$a]['device'], $sc[$i]['controllerType']);
  3714. // No such setting for this bus type
  3715. if(!$xtra) continue;
  3716. $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = $m->getExtraData($xtra);
  3717. if(trim($sc[$i]['mediumAttachments'][$a]['ignoreFlush']) === '')
  3718. $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = 1;
  3719. else
  3720. $sc[$i]['mediumAttachments'][$a]['ignoreFlush'] = $sc[$i]['mediumAttachments'][$a]['ignoreFlush'];
  3721. }
  3722. }
  3723. return $sc;
  3724. }
  3725. /**
  3726. * Check medium encryption password
  3727. *
  3728. * @param array $args array of arguments. See function body for details.
  3729. * @return array response data
  3730. */
  3731. public function remote_mediumCheckEncryptionPassword($args) {
  3732. // Connect to vboxwebsrv
  3733. $this->connect();
  3734. $m = $this->vbox->openMedium($args['medium'],'HardDisk');
  3735. $retval = $m->checkEncryptionPassword($args['password']);
  3736. $m->releaseRemote();
  3737. return $retval;
  3738. }
  3739. /**
  3740. * Change medium encryption
  3741. *
  3742. * @param array $args array of arguments. See function body for details.
  3743. * @return array response data containing progress id or true
  3744. */
  3745. public function remote_mediumChangeEncryption($args) {
  3746. // Connect to vboxwebsrv
  3747. $this->connect();
  3748. $m = $this->vbox->openMedium($args['medium'], 'HardDisk', 'ReadWrite');
  3749. /* @var $progress IProgress */
  3750. $progress = $m->changeEncryption($args['old_password'],
  3751. $args['cipher'], $args['password'], $args['id']);
  3752. // Does an exception exist?
  3753. try {
  3754. if($progress->errorInfo->handle) {
  3755. $this->errors[] = new Exception($progress->errorInfo->text);
  3756. $progress->releaseRemote();
  3757. $m->releaseRemote();
  3758. return false;
  3759. }
  3760. } catch (Exception $null) {
  3761. }
  3762. if($args['waitForCompletion']) {
  3763. $progress->waitForCompletion(-1);
  3764. $progress->releaseRemote();
  3765. $m->releaseRemote();
  3766. return true;
  3767. }
  3768. $this->_util_progressStore($progress);
  3769. return array('progress' => $progress->handle);
  3770. }
  3771. /**
  3772. * Resize a medium. Currently unimplemented in GUI.
  3773. *
  3774. * @param array $args array of arguments. See function body for details.
  3775. * @return array response data containing progress id
  3776. */
  3777. public function remote_mediumResize($args) {
  3778. // Connect to vboxwebsrv
  3779. $this->connect();
  3780. $m = $this->vbox->openMedium($args['medium'], 'HardDisk');
  3781. /* @var $progress IProgress */
  3782. $progress = $m->resize($args['bytes']);
  3783. // Does an exception exist?
  3784. try {
  3785. if($progress->errorInfo->handle) {
  3786. $this->errors[] = new Exception($progress->errorInfo->text);
  3787. $progress->releaseRemote();
  3788. return false;
  3789. }
  3790. } catch (Exception $null) {
  3791. }
  3792. $this->_util_progressStore($progress);
  3793. return array('progress' => $progress->handle);
  3794. }
  3795. /**
  3796. * Clone a medium
  3797. *
  3798. * @param array $args array of arguments. See function body for details.
  3799. * @return array response data containing progress id
  3800. */
  3801. public function remote_mediumCloneTo($args) {
  3802. // Connect to vboxwebsrv
  3803. $this->connect();
  3804. $format = strtoupper($args['format']);
  3805. /* @var $target IMedium */
  3806. $target = $this->vbox->createMedium($format, $args['location'], 'ReadWrite', 'HardDisk');
  3807. $mid = $target->id;
  3808. /* @var $src IMedium */
  3809. $src = $this->vbox->openMedium($args['src'], 'HardDisk');
  3810. $type = array(($args['type'] == 'fixed' ? 'Fixed' : 'Standard'));
  3811. if($args['split']) $type[] = 'VmdkSplit2G';
  3812. /* @var $progress IProgress */
  3813. $progress = $src->cloneTo($target->handle,$type,null);
  3814. $src->releaseRemote();
  3815. $target->releaseRemote();
  3816. // Does an exception exist?
  3817. try {
  3818. if($progress->errorInfo->handle) {
  3819. $this->errors[] = new Exception($progress->errorInfo->text);
  3820. $progress->releaseRemote();
  3821. return false;
  3822. }
  3823. } catch (Exception $null) {}
  3824. $this->_util_progressStore($progress);
  3825. return array('progress' => $progress->handle, 'id' => $mid);
  3826. }
  3827. /**
  3828. * Set medium to a specific type
  3829. *
  3830. * @param array $args array of arguments. See function body for details.
  3831. * @return boolean true on success
  3832. */
  3833. public function remote_mediumSetType($args) {
  3834. // Connect to vboxwebsrv
  3835. $this->connect();
  3836. /* @var $m IMedium */
  3837. $m = $this->vbox->openMedium($args['medium'], 'HardDisk');
  3838. $m->type = $args['type'];
  3839. $m->releaseRemote();
  3840. return true;
  3841. }
  3842. /**
  3843. * Add iSCSI medium
  3844. *
  3845. * @param array $args array of arguments. See function body for details.
  3846. * @return response data
  3847. */
  3848. public function remote_mediumAddISCSI($args) {
  3849. // Connect to vboxwebsrv
  3850. $this->connect();
  3851. // {'server':server,'port':port,'intnet':intnet,'target':target,'lun':lun,'enclun':enclun,'targetUser':user,'targetPass':pass}
  3852. // Fix LUN
  3853. $args['lun'] = intval($args['lun']);
  3854. if($args['enclun']) $args['lun'] = 'enc'.$args['lun'];
  3855. // Compose name
  3856. $name = $args['server'].'|'.$args['target'];
  3857. if($args['lun'] != 0 && $args['lun'] != 'enc0')
  3858. $name .= '|'.$args['lun'];
  3859. // Create disk
  3860. /* @var $hd IMedium */
  3861. $hd = $this->vbox->createMedium('iSCSI',$name, 'ReadWrite', 'HardDisk');
  3862. if($args['port']) $args['server'] .= ':'.intval($args['port']);
  3863. $arrProps = array();
  3864. $arrProps["TargetAddress"] = $args['server'];
  3865. $arrProps["TargetName"] = $args['target'];
  3866. $arrProps["LUN"] = $args['lun'];
  3867. if($args['targetUser']) $arrProps["InitiatorUsername"] = $args['targetUser'];
  3868. if($args['targetPass']) $arrProps["InitiatorSecret"] = $args['targetPass'];
  3869. if($args['intnet']) $arrProps["HostIPStack"] = '0';
  3870. $hd->setProperties(array_keys($arrProps),array_values($arrProps));
  3871. $hdid = $hd->id;
  3872. $hd->releaseRemote();
  3873. return array('id' => $hdid);
  3874. }
  3875. /**
  3876. * Add existing medium by file location
  3877. *
  3878. * @param array $args array of arguments. See function body for details.
  3879. * @return resposne data containing new medium's id
  3880. */
  3881. public function remote_mediumAdd($args) {
  3882. // Connect to vboxwebsrv
  3883. $this->connect();
  3884. /* @var $m IMedium */
  3885. $m = $this->vbox->openMedium($args['path'], $args['type'], 'ReadWrite', false);
  3886. $mid = $m->id;
  3887. $m->releaseRemote();
  3888. return array('id'=>$mid);
  3889. }
  3890. /**
  3891. * Get VirtualBox generated machine configuration file name
  3892. *
  3893. * @param array $args array of arguments. See function body for details.
  3894. * @return string filename
  3895. */
  3896. public function remote_vboxGetComposedMachineFilename($args) {
  3897. // Connect to vboxwebsrv
  3898. $this->connect();
  3899. return $this->vbox->composeMachineFilename($args['name'],($this->settings->phpVboxGroups ? '' : $args['group']),$this->vbox->systemProperties->defaultMachineFolder);
  3900. }
  3901. /**
  3902. * Create base storage medium (virtual hard disk)
  3903. *
  3904. * @param array $args array of arguments. See function body for details.
  3905. * @return response data containing progress id
  3906. */
  3907. public function remote_mediumCreateBaseStorage($args) {
  3908. // Connect to vboxwebsrv
  3909. $this->connect();
  3910. $format = strtoupper($args['format']);
  3911. $type = array(($args['type'] == 'fixed' ? 'Fixed' : 'Standard'));
  3912. if($args['split']) $type[] = 'VmdkSplit2G';
  3913. /* @var $hd IMedium */
  3914. $hd = $this->vbox->createMedium($format, $args['file'], 'ReadWrite', 'HardDisk');
  3915. /* @var $progress IProgress */
  3916. $progress = $hd->createBaseStorage(intval($args['size'])*1024*1024,$type);
  3917. // Does an exception exist?
  3918. try {
  3919. if($progress->errorInfo->handle) {
  3920. $this->errors[] = new Exception($progress->errorInfo->text);
  3921. $progress->releaseRemote();
  3922. return false;
  3923. }
  3924. } catch (Exception $null) {}
  3925. $this->_util_progressStore($progress);
  3926. $hd->releaseRemote();
  3927. return array('progress' => $progress->handle);
  3928. }
  3929. /**
  3930. * Release medium from all attachments
  3931. *
  3932. * @param array $args array of arguments. See function body for details.
  3933. * @return boolean true
  3934. */
  3935. public function remote_mediumRelease($args) {
  3936. // Connect to vboxwebsrv
  3937. $this->connect();
  3938. /* @var $m IMedium */
  3939. $m = $this->vbox->openMedium($args['medium'],$args['type']);
  3940. $mediumid = $m->id;
  3941. // connected to...
  3942. $machines = $m->machineIds;
  3943. $released = array();
  3944. foreach($machines as $uuid) {
  3945. // Find medium attachment
  3946. try {
  3947. /* @var $mach IMachine */
  3948. $mach = $this->vbox->findMachine($uuid);
  3949. } catch (Exception $e) {
  3950. $this->errors[] = $e;
  3951. continue;
  3952. }
  3953. $attach = $mach->mediumAttachments;
  3954. $remove = array();
  3955. foreach($attach as $a) {
  3956. if($a->medium->handle && $a->medium->id == $mediumid) {
  3957. $remove[] = array(
  3958. 'controller' => $a->controller,
  3959. 'port' => $a->port,
  3960. 'device' => $a->device);
  3961. break;
  3962. }
  3963. }
  3964. // save state
  3965. $state = (string)$mach->sessionState;
  3966. if(!count($remove)) continue;
  3967. $released[] = $uuid;
  3968. // create session
  3969. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  3970. // Hard disk requires machine to be stopped
  3971. if($args['type'] == 'HardDisk' || $state == 'Unlocked') {
  3972. $mach->lockMachine($this->session->handle, 'Write');
  3973. } else {
  3974. $mach->lockMachine($this->session->handle, 'Shared');
  3975. }
  3976. foreach($remove as $r) {
  3977. if($args['type'] == 'HardDisk') {
  3978. $this->session->machine->detachDevice($r['controller'],$r['port'],$r['device']);
  3979. } else {
  3980. $this->session->machine->mountMedium($r['controller'],$r['port'],$r['device'],null,true);
  3981. }
  3982. }
  3983. $this->session->machine->saveSettings();
  3984. $this->session->machine->releaseRemote();
  3985. $this->session->unlockMachine();
  3986. unset($this->session);
  3987. $mach->releaseRemote();
  3988. }
  3989. $m->releaseRemote();
  3990. return true;
  3991. }
  3992. /**
  3993. * Remove a medium
  3994. *
  3995. * @param array $args array of arguments. See function body for details.
  3996. * @return response data possibly containing progress operation id
  3997. */
  3998. public function remote_mediumRemove($args) {
  3999. // Connect to vboxwebsrv
  4000. $this->connect();
  4001. if(!$args['type']) $args['type'] = 'HardDisk';
  4002. /* @var $m IMedium */
  4003. $m = $this->vbox->openMedium($args['medium'],$args['type']);
  4004. if($args['delete'] && @$this->settings->deleteOnRemove && (string)$m->deviceType == 'HardDisk') {
  4005. /* @var $progress IProgress */
  4006. $progress = $m->deleteStorage();
  4007. $m->releaseRemote();
  4008. // Does an exception exist?
  4009. try {
  4010. if($progress->errorInfo->handle) {
  4011. $this->errors[] = new Exception($progress->errorInfo->text);
  4012. $progress->releaseRemote();
  4013. return false;
  4014. }
  4015. } catch (Exception $null) { }
  4016. $this->_util_progressStore($progress);
  4017. return array('progress' => $progress->handle);
  4018. } else {
  4019. $m->close();
  4020. $m->releaseRemote();
  4021. }
  4022. return true;
  4023. }
  4024. /**
  4025. * Get a list of recent media
  4026. *
  4027. * @param array $args array of arguments. See function body for details.
  4028. * @return array of recent media
  4029. */
  4030. public function remote_vboxRecentMediaGet($args) {
  4031. // Connect to vboxwebsrv
  4032. $this->connect();
  4033. $mlist = array();
  4034. foreach(array(
  4035. array('type'=>'HardDisk','key'=>'GUI/RecentListHD'),
  4036. array('type'=>'DVD','key'=>'GUI/RecentListCD'),
  4037. array('type'=>'Floppy','key'=>'GUI/RecentListFD')) as $r) {
  4038. $list = $this->vbox->getExtraData($r['key']);
  4039. $mlist[$r['type']] = array_filter(explode(';', trim($list,';')));
  4040. }
  4041. return $mlist;
  4042. }
  4043. /**
  4044. * Get a list of recent media paths
  4045. *
  4046. * @param array $args array of arguments. See function body for details.
  4047. * @return array of recent media paths
  4048. */
  4049. public function remote_vboxRecentMediaPathsGet($args) {
  4050. // Connect to vboxwebsrv
  4051. $this->connect();
  4052. $mlist = array();
  4053. foreach(array(
  4054. array('type'=>'HardDisk','key'=>'GUI/RecentFolderHD'),
  4055. array('type'=>'DVD','key'=>'GUI/RecentFolderCD'),
  4056. array('type'=>'Floppy','key'=>'GUI/RecentFolderFD')) as $r) {
  4057. $mlist[$r['type']] = $this->vbox->getExtraData($r['key']);
  4058. }
  4059. return $mlist;
  4060. }
  4061. /**
  4062. * Update recent medium path list
  4063. *
  4064. * @param array $args array of arguments. See function body for details.
  4065. * @return boolean true on success
  4066. */
  4067. public function remote_vboxRecentMediaPathSave($args) {
  4068. // Connect to vboxwebsrv
  4069. $this->connect();
  4070. $types = array(
  4071. 'HardDisk'=>'GUI/RecentFolderHD',
  4072. 'DVD'=>'GUI/RecentFolderCD',
  4073. 'Floppy'=>'GUI/RecentFolderFD'
  4074. );
  4075. $this->vbox->setExtraData($types[$args['type']], $args['folder']);
  4076. return true;
  4077. }
  4078. /**
  4079. * Update recent media list
  4080. *
  4081. * @param array $args array of arguments. See function body for details.
  4082. * @return boolean true on success
  4083. */
  4084. public function remote_vboxRecentMediaSave($args) {
  4085. // Connect to vboxwebsrv
  4086. $this->connect();
  4087. $types = array(
  4088. 'HardDisk'=>'GUI/RecentListHD',
  4089. 'DVD'=>'GUI/RecentListCD',
  4090. 'Floppy'=>'GUI/RecentListFD'
  4091. );
  4092. $this->vbox->setExtraData($types[$args['type']], implode(';',array_unique($args['list'])).';');
  4093. return true;
  4094. }
  4095. /**
  4096. * Mount a medium on the VM
  4097. *
  4098. * @param array $args array of arguments. See function body for details.
  4099. * @return boolean true on success
  4100. */
  4101. public function remote_mediumMount($args) {
  4102. // Connect to vboxwebsrv
  4103. $this->connect();
  4104. // Find medium attachment
  4105. /* @var $machine IMachine */
  4106. $machine = $this->vbox->findMachine($args['vm']);
  4107. $state = (string)$machine->sessionState;
  4108. // create session
  4109. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  4110. if($state == 'Unlocked') {
  4111. $machine->lockMachine($this->session->handle,'Write');
  4112. $save = true; // force save on closed session as it is not a "run-time" change
  4113. } else {
  4114. $machine->lockMachine($this->session->handle, 'Shared');
  4115. }
  4116. // Empty medium / eject
  4117. if($args['medium'] == 0) {
  4118. $med = null;
  4119. } else {
  4120. // Host drive
  4121. if(strtolower($args['medium']['hostDrive']) == 'true' || $args['medium']['hostDrive'] === true) {
  4122. // CD / DVD Drive
  4123. if($args['medium']['deviceType'] == 'DVD') {
  4124. $drives = $this->vbox->host->DVDDrives;
  4125. // floppy drives
  4126. } else {
  4127. $drives = $this->vbox->host->floppyDrives;
  4128. }
  4129. foreach($drives as $m) { /* @var $m IMedium */
  4130. if($m->id == $args['medium']['id']) {
  4131. /* @var $med IMedium */
  4132. $med = &$m;
  4133. break;
  4134. }
  4135. $m->releaseRemote();
  4136. }
  4137. // Normal medium
  4138. } else {
  4139. /* @var $med IMedium */
  4140. $med = $this->vbox->openMedium($args['medium']['location'],$args['medium']['deviceType']);
  4141. }
  4142. }
  4143. $this->session->machine->mountMedium($args['controller'],$args['port'],$args['device'],(is_object($med) ? $med->handle : null),true);
  4144. if(is_object($med)) $med->releaseRemote();
  4145. if($save) $this->session->machine->saveSettings();
  4146. $this->session->unlockMachine();
  4147. $machine->releaseRemote();
  4148. unset($this->session);
  4149. return true;
  4150. }
  4151. /**
  4152. * Get medium details
  4153. *
  4154. * @param IMedium $m medium instance
  4155. * @return array medium details
  4156. */
  4157. private function _mediumGetDetails(&$m) {
  4158. $children = array();
  4159. $attachedTo = array();
  4160. $machines = $m->machineIds;
  4161. $hasSnapshots = 0;
  4162. foreach($m->children as $c) { /* @var $c IMedium */
  4163. $children[] = $this->_mediumGetDetails($c);
  4164. $c->releaseRemote();
  4165. }
  4166. foreach($machines as $mid) {
  4167. $sids = $m->getSnapshotIds($mid);
  4168. try {
  4169. /* @var $mid IMachine */
  4170. $mid = $this->vbox->findMachine($mid);
  4171. } catch (Exception $e) {
  4172. $attachedTo[] = array('machine' => $mid .' ('.$e->getMessage().')', 'snapshots' => array());
  4173. continue;
  4174. }
  4175. $c = count($sids);
  4176. $hasSnapshots = max($hasSnapshots,$c);
  4177. for($i = 0; $i < $c; $i++) {
  4178. if($sids[$i] == $mid->id) {
  4179. unset($sids[$i]);
  4180. } else {
  4181. try {
  4182. /* @var $sn ISnapshot */
  4183. $sn = $mid->findSnapshot($sids[$i]);
  4184. $sids[$i] = $sn->name;
  4185. $sn->releaseRemote();
  4186. } catch(Exception $e) { }
  4187. }
  4188. }
  4189. $hasSnapshots = (count($sids) ? 1 : 0);
  4190. $attachedTo[] = array('machine'=>$mid->name,'snapshots'=>$sids);
  4191. $mid->releaseRemote();
  4192. }
  4193. // For $fixed value
  4194. $mvenum = new MediumVariant();
  4195. $variant = 0;
  4196. foreach($m->variant as $mv) {
  4197. $variant += $mvenum->ValueMap[(string)$mv];
  4198. }
  4199. // Encryption settings
  4200. $encryptionSettings = null;
  4201. if((string)$m->deviceType == 'HardDisk') {
  4202. try {
  4203. list($id, $cipher) = $m->getEncryptionSettings();
  4204. if($id) {
  4205. $encryptionSettings = array(
  4206. 'id' => $id,
  4207. 'cipher' => $cipher,
  4208. );
  4209. }
  4210. } catch (Exception $e) {
  4211. // Pass. Encryption is not configured
  4212. }
  4213. }
  4214. return array(
  4215. 'id' => $m->id,
  4216. 'description' => $m->description,
  4217. 'state' => (string)$m->refreshState(),
  4218. 'location' => $m->location,
  4219. 'name' => $m->name,
  4220. 'deviceType' => (string)$m->deviceType,
  4221. 'hostDrive' => $m->hostDrive,
  4222. 'size' => (string)$m->size, /* (string) to support large disks. Bypass integer limit */
  4223. 'format' => $m->format,
  4224. 'type' => (string)$m->type,
  4225. 'parent' => (((string)$m->deviceType == 'HardDisk' && $m->parent->handle) ? $m->parent->id : null),
  4226. 'children' => $children,
  4227. 'base' => (((string)$m->deviceType == 'HardDisk' && $m->base->handle) ? $m->base->id : null),
  4228. 'readOnly' => $m->readOnly,
  4229. 'logicalSize' => ($m->logicalSize/1024)/1024,
  4230. 'autoReset' => $m->autoReset,
  4231. 'hasSnapshots' => $hasSnapshots,
  4232. 'lastAccessError' => $m->lastAccessError,
  4233. 'variant' => $variant,
  4234. 'machineIds' => array(),
  4235. 'attachedTo' => $attachedTo,
  4236. 'encryptionSettings' => $encryptionSettings
  4237. );
  4238. }
  4239. /**
  4240. * Store a progress operation so that its status can be polled via progressGet()
  4241. *
  4242. * @param IProgress $progress progress operation instance
  4243. * @return string progress operation handle / id
  4244. */
  4245. private function _util_progressStore(&$progress) {
  4246. /* Store vbox and session handle */
  4247. $this->persistentRequest['vboxHandle'] = $this->vbox->handle;
  4248. if($this->session->handle) {
  4249. $this->persistentRequest['sessionHandle'] = $this->session->handle;
  4250. }
  4251. /* Store server if multiple servers are configured */
  4252. if(@is_array($this->settings->servers) && count($this->settings->servers) > 1)
  4253. $this->persistentRequest['vboxServer'] = $this->settings->name;
  4254. return $progress->handle;
  4255. }
  4256. /**
  4257. * Get VirtualBox system properties
  4258. * @param array $args array of arguments. See function body for details.
  4259. * @return array of system properties
  4260. */
  4261. public function remote_vboxSystemPropertiesGet($args) {
  4262. // Connect to vboxwebsrv
  4263. $this->connect();
  4264. $mediumFormats = array();
  4265. // Shorthand
  4266. $sp = $this->vbox->systemProperties;
  4267. // capabilities
  4268. $mfCap = new MediumFormatCapabilities(null,'');
  4269. foreach($sp->mediumFormats as $mf) { /* @var $mf IMediumFormat */
  4270. $exts = $mf->describeFileExtensions();
  4271. $dtypes = array();
  4272. foreach($exts[1] as $t) $dtypes[] = (string)$t;
  4273. $caps = array();
  4274. foreach($mf->capabilities as $c) {
  4275. $caps[] = (string)$c;
  4276. }
  4277. $mediumFormats[] = array('id'=>$mf->id,'name'=>$mf->name,'extensions'=>array_map('strtolower',$exts[0]),'deviceTypes'=>$dtypes,'capabilities'=>$caps);
  4278. }
  4279. $scs = array();
  4280. $scts = array('LsiLogic',
  4281. 'BusLogic',
  4282. 'IntelAhci',
  4283. 'PIIX4',
  4284. 'ICH6',
  4285. 'I82078',
  4286. 'USB');
  4287. foreach($scts as $t) {
  4288. $scs[$t] = $sp->getStorageControllerHotplugCapable($t);
  4289. }
  4290. return array(
  4291. 'minGuestRAM' => (string)$sp->minGuestRAM,
  4292. 'maxGuestRAM' => (string)$sp->maxGuestRAM,
  4293. 'minGuestVRAM' => (string)$sp->minGuestVRAM,
  4294. 'maxGuestVRAM' => (string)$sp->maxGuestVRAM,
  4295. 'minGuestCPUCount' => (string)$sp->minGuestCPUCount,
  4296. 'maxGuestCPUCount' => (string)$sp->maxGuestCPUCount,
  4297. 'autostartDatabasePath' => (@$this->settings->vboxAutostartConfig ? $sp->autostartDatabasePath : ''),
  4298. 'infoVDSize' => (string)$sp->infoVDSize,
  4299. 'networkAdapterCount' => 8, // static value for now
  4300. 'maxBootPosition' => (string)$sp->maxBootPosition,
  4301. 'defaultMachineFolder' => (string)$sp->defaultMachineFolder,
  4302. 'defaultHardDiskFormat' => (string)$sp->defaultHardDiskFormat,
  4303. 'homeFolder' => $this->vbox->homeFolder,
  4304. 'VRDEAuthLibrary' => (string)$sp->VRDEAuthLibrary,
  4305. 'defaultAudioDriver' => (string)$sp->defaultAudioDriver,
  4306. 'defaultVRDEExtPack' => $sp->defaultVRDEExtPack,
  4307. 'serialPortCount' => $sp->serialPortCount,
  4308. 'parallelPortCount' => $sp->parallelPortCount,
  4309. 'mediumFormats' => $mediumFormats,
  4310. 'scs' => $scs
  4311. );
  4312. }
  4313. /**
  4314. * Get a list of VM log file names
  4315. *
  4316. * @param array $args array of arguments. See function body for details.
  4317. * @return array of log file names
  4318. */
  4319. public function remote_machineGetLogFilesList($args) {
  4320. // Connect to vboxwebsrv
  4321. $this->connect();
  4322. /* @var $m IMachine */
  4323. $m = $this->vbox->findMachine($args['vm']);
  4324. $logs = array();
  4325. try { $i = 0; while($l = $m->queryLogFilename($i++)) $logs[] = $l;
  4326. } catch (Exception $null) {}
  4327. $lf = $m->logFolder;
  4328. $m->releaseRemote();
  4329. return array('path' => $lf, 'logs' => $logs);
  4330. }
  4331. /**
  4332. * Get VM log file contents
  4333. *
  4334. * @param array $args array of arguments. See function body for details.
  4335. * @return string log file contents
  4336. */
  4337. public function remote_machineGetLogFile($args) {
  4338. // Connect to vboxwebsrv
  4339. $this->connect();
  4340. /* @var $m IMachine */
  4341. $m = $this->vbox->findMachine($args['vm']);
  4342. $log = '';
  4343. try {
  4344. // Read in 8k chunks
  4345. while($l = $m->readLog(intval($args['log']),strlen($log),8192)) {
  4346. if(!count($l) || !strlen($l[0])) break;
  4347. $log .= base64_decode($l[0]);
  4348. }
  4349. } catch (Exception $null) {}
  4350. $m->releaseRemote();
  4351. // Attempt to UTF-8 encode string or json_encode may choke
  4352. // and return an empty string
  4353. if(function_exists('utf8_encode'))
  4354. return utf8_encode($log);
  4355. return $log;
  4356. }
  4357. /**
  4358. * Get a list of USB devices attached to a given VM
  4359. *
  4360. * @param array $args array of arguments. See function body for details.
  4361. * @return array list of devices
  4362. */
  4363. public function remote_consoleGetUSBDevices($args) {
  4364. // Connect to vboxwebsrv
  4365. $this->connect();
  4366. /* @var $machine IMachine */
  4367. $machine = $this->vbox->findMachine($args['vm']);
  4368. $this->session = $this->websessionManager->getSessionObject($this->vbox->handle);
  4369. $machine->lockMachine($this->session->handle, 'Shared');
  4370. $response = array();
  4371. foreach($this->session->console->USBDevices as $u) { /* @var $u IUSBDevice */
  4372. $response[$u->id] = array('id'=>$u->id,'remote'=>$u->remote);
  4373. $u->releaseRemote();
  4374. }
  4375. $this->session->unlockMachine();
  4376. unset($this->session);
  4377. $machine->releaseRemote();
  4378. return $response;
  4379. }
  4380. /**
  4381. * Return a string representing the VirtualBox ExtraData key
  4382. * for this port + device + bus type IgnoreFlush setting
  4383. *
  4384. * @param integer port medium attachment port number
  4385. * @param integer device medium attachment device number
  4386. * @param string cType controller type
  4387. * @return string extra data setting string
  4388. */
  4389. private function _util_getIgnoreFlushKey($port,$device,$cType) {
  4390. $cTypes = array(
  4391. 'piix3' => 'piix3ide',
  4392. 'piix4' => 'piix3ide',
  4393. 'ich6' => 'piix3ide',
  4394. 'intelahci' => 'ahci',
  4395. 'lsilogic' => 'lsilogicscsi',
  4396. 'buslogic' => 'buslogic',
  4397. 'lsilogicsas' => 'lsilogicsas'
  4398. );
  4399. if(!isset($cTypes[strtolower($cType)])) {
  4400. $this->errors[] = new Exception('Invalid controller type: ' . $cType);
  4401. return '';
  4402. }
  4403. $lun = ((intval($device)*2) + intval($port));
  4404. return str_replace('[b]',$lun,str_replace('[a]',$cTypes[strtolower($cType)],"VBoxInternal/Devices/[a]/0/LUN#[b]/Config/IgnoreFlush"));
  4405. }
  4406. /**
  4407. * Get a newly generated MAC address from VirtualBox
  4408. *
  4409. * @param array $args array of arguments. See function body for details
  4410. * @return string mac address
  4411. */
  4412. public function remote_vboxGenerateMacAddress($args) {
  4413. // Connect to vboxwebsrv
  4414. $this->connect();
  4415. return $this->vbox->host->generateMACAddress();
  4416. }
  4417. /**
  4418. * Set group definition
  4419. *
  4420. * @param array $args array of arguments. See function body for details
  4421. * @return boolean true on success
  4422. */
  4423. public function remote_vboxGroupDefinitionsSet($args) {
  4424. $this->connect();
  4425. // Save a list of valid paths
  4426. $validGroupPaths = array();
  4427. $groupKey = ($this->settings->phpVboxGroups ? vboxconnector::phpVboxGroupKey : 'GUI/GroupDefinitions');
  4428. // Write out each group definition
  4429. foreach($args['groupDefinitions'] as $groupDef) {
  4430. $this->vbox->setExtraData($groupKey.$groupDef['path'], $groupDef['order']);
  4431. $validGroupPaths[] = $groupDef['path'];
  4432. }
  4433. // Remove any unused group definitions
  4434. $keys = $this->vbox->getExtraDataKeys();
  4435. foreach($keys as $k) {
  4436. if(strpos($k,$groupKey) !== 0) continue;
  4437. if(array_search(substr($k,strlen($groupKey)), $validGroupPaths) === false)
  4438. $this->vbox->setExtraData($k,'');
  4439. }
  4440. return true;
  4441. }
  4442. /**
  4443. * Return group definitions
  4444. *
  4445. * @param array $args array of arguments. See function body for details
  4446. * @return array group definitions
  4447. */
  4448. public function remote_vboxGroupDefinitionsGet($args) {
  4449. $this->connect();
  4450. $response = array();
  4451. $keys = $this->vbox->getExtraDataKeys();
  4452. $groupKey = ($this->settings->phpVboxGroups ? vboxconnector::phpVboxGroupKey : 'GUI/GroupDefinitions');
  4453. foreach($keys as $grouppath) {
  4454. if(strpos($grouppath,$groupKey) !== 0) continue;
  4455. $subgroups = array();
  4456. $machines = array();
  4457. $response[] = array(
  4458. 'name' => substr($grouppath,strrpos($grouppath,'/')+1),
  4459. 'path' => substr($grouppath,strlen($groupKey)),
  4460. 'order' => $this->vbox->getExtraData($grouppath)
  4461. );
  4462. }
  4463. return $response;
  4464. }
  4465. /**
  4466. * Format a time span in seconds into days / hours / minutes / seconds
  4467. * @param integer $t number of seconds
  4468. * @return array containing number of days / hours / minutes / seconds
  4469. */
  4470. private function _util_splitTime($t) {
  4471. $spans = array(
  4472. 'days' => 86400,
  4473. 'hours' => 3600,
  4474. 'minutes' => 60,
  4475. 'seconds' => 1);
  4476. $time = array();
  4477. foreach($spans as $k => $v) {
  4478. if(!(floor($t / $v) > 0)) continue;
  4479. $time[$k] = floor($t / $v);
  4480. $t -= floor($time[$k] * $v);
  4481. }
  4482. return $time;
  4483. }
  4484. /**
  4485. * Return VBOX result code text for result code
  4486. *
  4487. * @param integer result code number
  4488. * @return string result code text
  4489. */
  4490. private function _util_resultCodeText($c) {
  4491. $rcodes = new ReflectionClass('VirtualBox_COM_result_codes');
  4492. $rcodes = array_flip($rcodes->getConstants());
  4493. $rcodes['0x80004005'] = 'NS_ERROR_FAILURE';
  4494. return @$rcodes['0x'.strtoupper(dechex($c))] . ' (0x'.strtoupper(dechex($c)).')';
  4495. }
  4496. }