| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783 |
- <!DOCTYPE html>
- <html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
- <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
- <meta name="format-detection" content="telephone=no" />
- <meta name="robots" content="noindex,nofollow">
- <link rel="manifest" href="{{{domainurl}}}manifest.json">
- <link rel="shortcut icon" href="{{{domainurl}}}favicon.ico" />
- <link rel="icon" type="image/png" sizes="16x16" href="{{{domainurl}}}favicon-16x16.png">
- <link rel="icon" type="image/png" sizes="32x32" href="{{{domainurl}}}favicon-32x32.png">
- <link rel="apple-touch-icon" href="/favicon-303x303.png" />
- <link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
- {{{customCSSTags}}}
- <meta name="apple-mobile-web-app-capable" content="yes">
- <meta name="apple-mobile-web-app-status-bar-style" content="#ffffff">
- <meta name="apple-mobile-web-app-title" content="{{{title}}}">
- <script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/agent-desktop-0.0.2{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/amt-0.2.0{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/amt-redir-ws-0.1.0{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/amt-desktop-0.0.2{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/zlib{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/zlib-inflate{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script>
- <script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script>
- <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
- {{{customJSTags}}}
- <meta name="msapplication-TileColor" content="#00aba9">
- <meta name="theme-color" content="#ffffff">
- <title>{{{title}}}</title>
- <style>
- body {
- background-color: white;
- }
- .night body {
- background-color: black;
- }
- #MxMESH {
- color: black;
- }
- .night #MxMESH {
- color: lightgray;
- }
- .textOverGray { color: black; }
- #dialog {
- z-index:1000;
- background-color:#EEE;
- box-shadow:0px 0px 15px #666;
- font-family:Arial,Helvetica,sans-serif;
- border-radius:5px;
- position:fixed;
- top:90px;
- width:300px;
- }
- .night #dialog {
- color: black;
- background-color:#AAA;
- }
- :focus {
- outline: 0;
- }
- a {
- color: #036;
- text-decoration: underline;
- }
- .night a {
- color: #99F;
- }
- #footer a {
- color: #fff;
- text-decoration: underline;
- }
- #footer a:hover {
- text-decoration: none;
- }
- .night #footer {
- color: gray;
- }
- .i1 {
- background: url(../images/icons50.png) 0px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i2 {
- background: url(../images/icons50.png) -50px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i3 {
- background: url(../images/icons50.png) -100px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i4 {
- background: url(../images/icons50.png) -150px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i5 {
- background: url(../images/icons50.png) -200px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i6 {
- background: url(../images/icons50.png) -250px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i7 {
- background: url(../images/icons50.png) -300px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .i8 {
- background: url(../images/icons50.png) -350px 0px;
- background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x);
- height: 50px;
- width: 50px;
- border: none;
- }
- .m0 {
- background: url(../images/images16.png) -32px 0px;
- height: 16px;
- width: 16px;
- border: none;
- float: left;
- }
- .m1 {
- background: url(../images/images16.png) -16px 0px;
- height: 16px;
- width: 16px;
- border: none;
- float: left;
- }
- .m2 {
- background: url(../images/images16.png) -96px 0px;
- height: 16px;
- width: 16px;
- border: none;
- float: left;
- }
- .m3 {
- background: url(../images/images16.png) -112px 0px;
- height: 16px;
- width: 16px;
- border: none;
- float: left;
- }
- .m4 {
- background: url(../images/images16.png) -128px 0px;
- height: 16px;
- width: 16px;
- border: none;
- float: left;
- }
- .NotifyIconSmall1 { width:24px; height:24px; background: url(../images/notify24.png) 0px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall2 { width:24px; height:24px; background: url(../images/notify24.png) -24px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall3 { width:24px; height:24px; background: url(../images/notify24.png) -48px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall4 { width:24px; height:24px; background: url(../images/notify24.png) -72px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall5 { width:24px; height:24px; background: url(../images/notify24.png) -96px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall6 { width:24px; height:24px; background: url(../images/notify24.png) -120px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall7 { width:24px; height:24px; background: url(../images/notify24.png) -144px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall8 { width:24px; height:24px; background: url(../images/notify24.png) -168px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .NotifyIconSmall9 { width:24px; height:24px; background: url(../images/notify24.png) -192px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); }
- .gray {
- /*filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");*/ /* Firefox 10+, Firefox on Android */
- filter: gray; /* IE6-9 */
- -webkit-filter: grayscale(100%) opacity(60%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */
- }
- .DevSt {
- padding-left: 5px;
- border-bottom-style: solid;
- border-bottom-width: 1px;
- border-bottom-color: #DDDDDD;
- }
- .noselect {
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
- .fileIcon1 {
- background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb49Y2Sj9LT2f///yH5BAEAAAMALAAAAAAQABAAAAImnI+py+1vhJwyUYAzHTL4D3qdlJWaIFJqmKod607sDKIiDUP63hQAOw==);
- height: 16px;
- width: 16px;
- cursor: pointer;
- border: none;
- float: left;
- margin-top: 1px;
- }
- .fileIcon2 {
- background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAM2xV/Xur+XPgP///yH5BAEAAAMALAAAAAAQABAAAAJD3ISZIGHWUGihznesYDYATFVM+D2hJ4lgN1olxALAtAlmPCJvuMmJd6PJckDYwicrHhTD5o7plJmg0Uc0asNMkphHAQA7);
- height: 16px;
- width: 16px;
- cursor: pointer;
- border: none;
- float: left;
- margin-top: 1px;
- }
- .fileIcon3 {
- background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb19IGBgbq6uv///yH5BAEAAAMALAAAAAAQABAAAAIy3ISpxgcPH2ouQgFEw1YmxnUXKEaaEZZnVWZk66JwzKpvuwZzwOgwb/C1gIOA8Yg8DgoAOw==);
- height: 16px;
- width: 16px;
- cursor: pointer;
- border: none;
- float: left;
- margin-top: 1px;
- }
- .fileIcon4 {
- background: url(../images/meshicon16.png);
- height: 16px;
- width: 16px;
- cursor: pointer;
- border: none;
- float: left;
- margin-top: 1px;
- }
- .filelist {
- -moz-user-select: none;
- -khtml-user-select: none;
- -webkit-user-select: none;
- -o-user-select: none;
- cursor: default;
- -khtml-user-drag: element;
- clear: both;
- }
- .deviceNotifyDot {
- position:absolute;
- right:10px;
- top:0px;
- height:16px;
- }
- .deviceNotifyDotSub {
- text-align:center;
- color:#FFF;
- width:16px;
- background-color:#00F;
- padding:2px;
- border-radius:10px;
- box-shadow: 2px 2px 10px black;
- cursor:pointer;
- margin-left:3px;
- float:left;
- }
- .deviceNotifyDotSub:hover {
- background-color:#44F;
- }
- .deviceNotifySmallDot {
- position:absolute;
- right:10px;
- top:0px;
- height:10px;
- }
- .deviceNotifySmallDotSub {
- text-align:center;
- color:#FFF;
- width:10px;
- padding:2px;
- background-color:#00F;
- border-radius:10px;
- box-shadow: 2px 2px 10px black;
- cursor:pointer;
- margin-left:2px;
- float:left;
- }
- .deviceNotifySmallDotSub:hover {
- background-color:#44F;
- }
- .deviceNotifyLargeDot {
- position:absolute;
- right:10px;
- top:10px;
- height:40px;
- }
- .deviceNotifyLargeDotSub {
- text-align:center;
- width:35px;
- height:35px;
- color:#FFF;
- padding:2px;
- background-color:#00F;
- border-radius:20px;
- box-shadow: 2px 2px 10px black;
- cursor:pointer;
- margin-left:4px;
- font-size:30px;
- float:left;
- }
- .deviceNotifyLargeDotSub:hover {
- background-color:#44F;
- }
- .style10 {
- background-color: #C9C9C9;
- color: #000;
- }
- .night .style10 {
- background-color: #888;
- }
- .deviceBatteryLarge {
- position:absolute;
- right:10px;
- top:0px;
- width:28px;
- height:48px;
- border:none;
- box-shadow:none;
- }
- .deviceBatteryLarge1 { background: url(../images/batteries48.png) 0px 0px; }
- .deviceBatteryLarge2 { background: url(../images/batteries48.png) -28px 0px; }
- .deviceBatteryLarge3 { background: url(../images/batteries48.png) -56px 0px; }
- .deviceBatteryLarge4 { background: url(../images/batteries48.png) -84px 0px; }
- .deviceBatteryLarge5 { background: url(../images/batteries48.png) -112px 0px; }
- .deviceBatteryLarge6 { background: url(../images/batteries48.png) -140px 0px; }
- .deviceBatteryLarge7 { background: url(../images/batteries48.png) -168px 0px; }
- .deviceBatteryLarge8 { background: url(../images/batteries48.png) -196px 0px; }
- .deviceBatteryLarge9 { background: url(../images/batteries48.png) -224px 0px; }
- .deviceBatteryLarge10 { background: url(../images/batteries48.png) -252px 0px; }
- .deviceBatteryLarge11 { background: url(../images/batteries48.png) -280px 0px; }
- .deviceBatterySmall {
- position:absolute;
- left:6px;
- top:22px;
- width:14px;
- height:24px;
- border:none;
- box-shadow:none;
- }
- .deviceBatterySmall1 { background: url(../images/batteries24.png) 0px 0px; }
- .deviceBatterySmall2 { background: url(../images/batteries24.png) -14px 0px; }
- .deviceBatterySmall3 { background: url(../images/batteries24.png) -28px 0px; }
- .deviceBatterySmall4 { background: url(../images/batteries24.png) -42px 0px; }
- .deviceBatterySmall5 { background: url(../images/batteries24.png) -56px 0px; }
- .deviceBatterySmall6 { background: url(../images/batteries24.png) -70px 0px; }
- .deviceBatterySmall7 { background: url(../images/batteries24.png) -84px 0px; }
- .deviceBatterySmall8 { background: url(../images/batteries24.png) -98px 0px; }
- .deviceBatterySmall9 { background: url(../images/batteries24.png) -112px 0px; }
- .deviceBatterySmall10 { background: url(../images/batteries24.png) -126px 0px; }
- .deviceBatterySmall11 { background: url(../images/batteries24.png) -140px 0px; }
- .meshList {
- width:auto;
- height:40px;
- background-color:lightgray;
- margin-top:5px;
- margin-bottom:5px;
- margin-left:60px;
- padding-top:5px;
- padding-bottom:5px;
- border-radius:8px 0px 0px 8px;
- }
- .night .meshList {
- background-color: gray;
- }
- .devList1 {
- height: 50px;
- cursor: pointer;
- position: relative;
- margin-top: 5px;
- margin-bottom: 5px;
- }
- .devList2 {
- float: left;
- margin-left: 4px
- }
- .devList3 {
- width: auto;
- height: 40px;
- background-color: lightgray;
- margin-left: 60px;
- padding-top: 5px;
- padding-bottom: 5px;
- border-radius: 8px 0px 0px 8px;
- }
- .night .devList3 {
- background-color: gray;
- }
- .devList4 {
- padding-left: 12px;
- padding-top: 2px;
- color: black;
- }
- .devList5 {
- padding-left: 12px;
- padding-top: 3px;
- color: #444
- }
- .night .devList5 {
- color: black;
- }
- .deskButton {
- box-shadow: 0px 0px 10px #000;
- border-radius:20px;
- position:absolute;
- right:10px;
- top:10px;
- cursor:pointer;
- background-color:#AAA;
- z-index:1000;
- }
- .menuButton{
- box-shadow: 0px 0px 10px #000;
- border-radius:10px;
- display:inline-block;
- width:120px;
- background-color:#AAA;
- text-align:center;
- padding:8px;
- cursor:pointer;
- margin:10px;
- z-index:1000;
- }
- #notificationCount {
- min-width: 28px;
- font-size: 20px;
- background-color: orange;
- text-align: center;
- cursor: pointer;
- color: black;
- }
- .notifiyBox {
- font-size: 16px;
- position: absolute;
- z-index: 1000;
- top: 60px;
- right: 76px;
- width: 300px;
- text-align: left;
- background-color: #F0ECCD;
- border: 4px solid #666;
- -webkit-border-radius: 10px;
- -moz-border-radius: 10px;
- border-radius: 10px;
- -webkit-box-shadow: 2px 2px 4px #888;
- -moz-box-shadow: 2px 2px 4px #888;
- box-shadow: 2px 2px 4px #888;
- max-height: 200px;
- }
- .night .notifiyBox {
- color: black;
- }
- .notifiyBox:before {
- content: ' ';
- position: absolute;
- width: 0;
- height: 0;
- right: 5px;
- top: -30px;
- border: 15px solid;
- border-color: transparent #666 #666 transparent;
- }
- .notifiyBox:after {
- content: ' ';
- position: absolute;
- width: 0;
- height: 0;
- right: 7px;
- top: -24px;
- border: 12px solid;
- border-color: transparent #F0ECCD #F0ECCD transparent;
- }
- #p15statetext {
- padding: 4px;
- height: 15px;
- }
- #p15agentConsole {
- background: black;
- margin: 0;
- padding: 0;
- color: lightgray;
- width: 100%;
- position: relative;
- }
- #p15coreName {
- padding: 4px;
- display: inline-block;
- }
- #p15agentConsoleText {
- position:absolute;
- margin: 0;
- padding: 0;
- top: 0;
- bottom: 0;
- left:0;
- right: 0;
- overflow-y: scroll;
- overflow-x: auto;
- }
- .areaHead {
- padding-top: 2px;
- padding-bottom: 2px;
- background: #C0C0C0;
- }
- .night .areaHead {
- color: #CCC;
- background: #333;
- }
- .areaFoot {
- padding-top: 2px;
- padding-bottom: 2px;
- background: #C0C0C0;
- }
- .night .areaFoot {
- color: #CCC;
- background: #333;
- }
- .toright2 {
- float: right;
- text-align: right;
- }
- #consoleTable {
- width: 100%;
- height: 100%;
- padding: 0px;
- margin-top: 0px;
- }
- .night #consoleTable {
- color: black;
- }
- .menucurve {
- background-color:white;
- width:10px;
- height:10px;
- border-radius:10px 0 0 0;
- border-right:1px solid white;
- border-bottom:1px solid white;
- }
- .night .menucurve {
- background-color:black;
- border-right:1px solid black;
- border-bottom:1px solid black;
- }
- #termTable {
- width: 100%;
- padding: 0px;
- margin-top: 0px;
- }
- .fulldesk #termTable {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- }
- #termarea3x {
- background: black;
- text-align: center;
- height: 400px;
- position: relative;
- }
- .viewSelector10 {
- margin-left: 2px;
- margin-top: 2px;
- background: url(../images/views.png) -476px 0px;
- height: 28px;
- width: 28px;
- }
- .viewSelector11 {
- margin-left: 2px;
- margin-top: 2px;
- background: url(../images/views.png) -504px 0px;
- height: 28px;
- width: 28px;
- }
- .tagSpan {
- background-color: lightgray;
- padding: 3px;
- border-radius: 5px;
- }
- .night .tagSpan {
- color: black;
- }
- #d3serveraction, #d2serveraction {
- width: 100%;
- background-color: #d3d9d6;
- text-align: left;
- padding: 3px;
- }
- #d3serverfiles, #d2serverfiles {
- width: 100%;
- height: 150px;
- background-color: white;
- padding: 2px;
- border: 1px solid gray;
- overflow-y: scroll;
- }
- </style>
- </head>
- <body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
- <div id=container>
- <div id="notifiyBox" class="notifiyBox" style="display:none"></div>
- <div id=mastheadx></div>
- <div id=masthead style="background:url(logo.png) 0px 0px;background-size:341px 50px;background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden">
- <div style="width:calc(100% - 50px);overflow:hidden">
- <div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:6px" onclick="go(2)">
- <strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif;text-shadow: 1px 1px 2px #000;">{{{title1}}}</font></strong>
- </div>
- <div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:10px">
- <strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif;text-shadow: 1px 1px 2px #000;">{{{title2}}}</font></strong>
- </div>
- </div>
- <div id="devViewPageState" class=noselect style="position:absolute;right:160px;top:10px;height:30px;line-height:30px;color:#c8c8c8;font-size:16px;display:none"></div>
- <img id="devViewPageButton2" class=noselect style="position:absolute;right:130px;top:10px;cursor:pointer;display:none" onclick=onDeviceViewPageChange(2) src="/images/left-30.png" width=20 height=30 />
- <img id="devViewPageButton3" class=noselect style="position:absolute;right:100px;top:10px;cursor:pointer;display:none" onclick=onDeviceViewPageChange(3) src="/images/right-30.png" width=20 height=30 />
- <div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="position:absolute;right:50px;top:0px;font-size:28px;width:50px;height:50px;cursor:pointer;display:none" title="Click to view current notifications"><div id="notificationCount2" style="padding-top:8px">0</div></div>
- <img id="topMenuIcon" class=noselect style="position:absolute;right:0;top:10px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer;display:none" onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 />
- </div>
- <div id=page_content style="position:absolute;bottom:32px;top:50px;width:100%">
- <div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px">
- <div id=p0 style=display:none;width:100%;height:100%>
- <div style="display:flex;align-items:center;width:100%;height:100%">
- <div id=p0message style=text-align:center;width:100%><span id="p0span">Server disconnected</span>, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div>
- </div>
- </div>
- <div id=p1 style=display:none;width:100%;height:100%>
- <div style="display:flex;align-items:center;width:100%;height:100%">
- <div id=p1message style=text-align:center;width:100%></div>
- </div>
- </div>
- <div id=p2 style="display:none;position:absolute;top:0;left:0;right:0;bottom:0">
- <div id=xdevices style="position:absolute;overflow-y:auto;top:0;left:0;right:0;bottom:34px" onscroll="onDevicesScroll()" ontouchstart="onDeviceTouch(true)" ontouchend="onDeviceTouch(false)"></div>
- <div id=xdevicesBar style="position:absolute;overflow-y:auto;height:34px;left:0;right:0;bottom:0px;background-color:#aaa;color:black">
- <div style="margin:4px">
- <span style="width:20px;display:inline-block;text-align:center;cursor:pointer;font-size:16px" onclick=clearSearchInput()><b>X</b></span>
- <input id=SearchInput autocomplete=off type=search placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) style="padding:2px;margin:0;height:20px;background-color:#FFF" />
- <label class=noselect><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() />OS Name</label>
- <label class=noselect><input type=checkbox id=OnlineCheckBox onclick=onOnlineCheckBox(event) />Online</label>
- </div>
- </div>
- </div>
- <div id=p3 style=display:none;position:absolute;bottom:0;top:0;width:100%>
- <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0">
- <tr style=padding:0>
- <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
- <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
- <div class="menucurve"></div>
- </div>
- <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div>
- </td>
- <td>
- <div style=margin-left:5px>
- <strong style="font-size:large"><span id=p3userName></span></strong><br />
- </div>
- </td>
- </tr>
- </table>
- <div id=p3info style="overflow-y:auto;position:absolute;top:55px;bottom:0px;width:100%">
- <img id="p2AccountImage" alt="" width="128" height="128" onclick="account_manageImage(0)" src="images/user-256.png" style="position:absolute;right:8px;top:7px;border-radius:8px;box-shadow:0px 0px 7px #000" />
- <div style="margin-left:8px">
- <div id="p3AccountActions">
- <div id="p2AccountSecurity" style="display:none">
- <p><strong>Account Security</strong></p>
- <div style="margin-left:9px;margin-bottom:8px">
- <div id="managePhoneNumber1" style="margin-top:5px;display:none"><a onclick="account_managePhone()" style="cursor:pointer">Manage phone number</a> <span id="authPhoneNumberCheck"><strong>✓</strong></span></div>
- <div id="manageEmail2FA" style="margin-top:5px;display:none"><a onclick="account_manageAuthEmail()" style="cursor:pointer">Manage email authentication</a> <span id="authEmailSetupCheck"><strong>✓</strong></span></div>
- <div style="margin-top:5px"><a href=# onclick="account_showLocalizationSettings()">Localization Settings</a></div>
- <div id="manageAuthApp" style="margin-top:5px;display:none"><a onclick="account_manageAuthApp()" style="cursor:pointer">Manage authenticator app</a> <span id="authAppSetupCheck"><strong>✓</strong></span></div>
- <div id="manageOtp" style="margin-top:5px;display:none"><a onclick="account_manageOtp(0)" style="cursor:pointer">Manage backup codes</a> <span id="authCodesSetupCheck"><strong>✓</strong></span></div>
- </div>
- </div>
- <div id="p2AccountActions" style="display:none">
- <p><strong>Account Actions</strong></p>
- <div style="margin-left:9px;margin-bottom:8px">
- <div style="margin-top:5px"><span id="viewPreviousLogins"><a onclick="return account_viewPreviousLogins()" style="cursor:pointer">View previous logins</a></span></div>
- <div style="margin-top:5px"><span id="managePhoneNumber2" style="display:none"><a onclick="account_managePhone()" style="cursor:pointer">Manage phone number</a></span></div>
- <div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div>
- <span id="p2AccountPassActions">
- <div style="margin-top:5px"><span id="changeEmailId" style="display:none"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></span></div>
- <div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span></div>
- <div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div>
- </span>
- <div style="margin-top:5px" id="setDarkModeLink"><a onclick="toggleNightMode()" style="cursor:pointer">Set dark mode</a></div>
- <div style="margin-top:5px"><a onclick="showNotes(false)" style="cursor:pointer">Personal notes</a></div>
- </div>
- <br style=clear:both />
- </div>
- </div>
- <strong>Device Groups</strong>
- <span id="p3createMeshLink1">( <a onclick=account_createMesh() style=cursor:pointer><img src="images/icon-addnew.png" width=12 height=12 border=0 /> New</a> )</span>
- <br /><br />
- <div id=p3meshes></div>
- <div id=p3noMeshFound style=margin-left:9px;display:none>No device groups.<span id="p3createMeshLink2"> <a onclick=account_createMesh() style=cursor:pointer><strong>Get started here!</strong></a></span></div>
- <br style=clear:both />
- </div>
- </div>
- </div>
- <div id=p5 style=display:none>
- <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;">
- <tr style=padding:0>
- <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
- <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
- <div class="menucurve"></div>
- </div>
- <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div>
- </td>
- <td>
- <img src="/images/user-50.png" width=50 height=50 />
- </td>
- <td>
- <div style=margin-left:5px>
- <strong style="font-size:large">My Files</strong><br />
- </div>
- </td>
- </tr>
- </table>
- <div id=p5myfiles style="position:absolute;top:55px;bottom:0px;width:100%">
- <table id="p5toolbar" style="width:100%;height:78px" cellpadding="0" cellspacing="0">
- <tr>
- <td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom>
- <div style="width:100%;text-align:center">
- <input type=button style="width:calc(100%/5 - 5px)" id=p5FolderUp disabled="disabled" onclick="p5folderup()" value="Up" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5SelectAllButton disabled="disabled" onclick="p5selectallfile()" value="SelectAll" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5RenameFileButton disabled="disabled" value="Rename" onclick="p5renamefile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5DeleteFileButton disabled="disabled" value="Delete" onclick="p5deletefile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5NewFolderButton disabled="disabled" value="Folder" onclick="p5createfolder()" onkeypress="return false" onkeydown="return false" />
- </div>
- <div style="width:100%;text-align:center">
- <input type=button style="width:calc(100%/5 - 5px)" id=p5UploadButton disabled="disabled" value="Upload" onclick="p5uploadFile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5CutButton disabled="disabled" value="Cut" onclick="p5copyFile(1)" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5CopyButton disabled="disabled" value="Copy" onclick="p5copyFile(0)" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5PasteButton disabled="disabled" value="Paste" onclick="p5pasteFile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p5RefreshButton value="Refresh" onclick="p5refreshFiles()" onkeypress="return false" onkeydown="return false" />
- </div>
- </td>
- </tr>
- <tr>
- <td style="background-color:#E4E9E7;height:28px">
- <table style="width:100%">
- <tr>
- <td id=p5currentpath style="overflow:hidden;padding-left:4px;padding-top:2px;color:black"></td>
- <td style="text-align:right;padding-right:4px">
- <select id=p5sortdropdown onchange=updateFiles()>
- <option value=1 selected="selected">Sort by name</option>
- <option value=2>Sort by size</option>
- <option value=3>Sort by date</option>
- <option value=4>Descend by name</option>
- <option value=5>Descend by size</option>
- <option value=6>Descend by date</option>
- </select>
- </td>
- </tr>
- </table>
- </td>
- </tr>
- </table>
- <div id="p5filetable" style="width:100%;height:calc(100% - 102px);overflow:auto;-webkit-user-select:none">
- <!--
- <div id="p5bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✓</b></div>
- <div id="p5bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✗</b></div>
- -->
- <span id="p5files"></span>
- </div>
- <table id="p5toolbarBottom" style="width:100%;height:22px;position:absolute;bottom:0px;background-color:#D3D9D6" cellpadding=0 cellspacing=0>
- <tr>
- <td style="text-align:left;padding:3px"> <span id="p5bottomstatus"></span></td>
- <td id="p5rightOfButtons" style="text-align:right;padding:3px"></td>
- </tr>
- </table>
- </div>
- </div>
- <div id=p10 style=display:none;position:absolute;bottom:0;top:0;width:100%;overflow:hidden>
- <table id=p10deskTopTable cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0">
- <tr style=padding:0>
- <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
- <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
- <div class="menucurve"></div>
- </div>
- <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div>
- </td>
- <td>
- <a id=MainComputerImage style=cursor:pointer onclick=p10showiconselector()></a>
- </td>
- <td>
- <div style=margin-left:5px>
- <strong><span id=p10deviceName></span></strong><br />
- <span id=MainComputerState></span>
- </div>
- </td>
- </tr>
- </table>
- <div id=p10dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:30px;width:300px;left:30px;display:none">
- <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
- <div style=padding:5px>Keyboard Shortcuts Customization</div>
- <div style=width:100%;margin:6px></div>
- </div>
- <div style="margin-right:16px;margin-left:8px"><div id=p10dialog2 style="margin:auto;margin:3px"></div></div>
- <div style="width:100%;padding:2px;text-align:center"><input type=button value="Restore Default Keyboard Shortcuts" onclick=restoreDeskCustomizeKey() /></div>
- <div style="padding:10px;margin-bottom:20px"><input type="button" value="OK" style="float:right;width:80px" onclick="deskCustomizeKeysEx()"></div>
- </div>
- <div id=p10general style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
- <div class="deviceNotifyLargeDot">
- <img id="p10deviceStar" class=deviceNotifyLargeDotSub src=images/icon-star-notify-40.png width=35 height=35>
- <div id="p10deviceMsg" onclick=showDeviceMessages(null,null,event) class=deviceNotifyLargeDotSub></div>
- <img id="p10deviceNotify" onclick=showDeviceSessions() class=deviceNotifyLargeDotSub src=images/icon-relay-notify-40.png width=35 height=35>
- <img id="p10deviceHelp" onclick=showDeviceHelpRequests(null,null,event) class=deviceNotifyLargeDotSub src=images/icon-help-notify-40.png width=35 height=35>
- </div>
- <div id="p10deviceBattery" class="deviceBatteryLarge deviceBatteryLarge1"></div>
- <div id=p10html style="margin-left:8px;margin-right:8px"></div>
- <div id=p10html2></div>
- <div id=p10html3 style="margin-left:8px"></div>
- </div>
- <img id="deskkeybutton1" src="images/mobile-desk-exit.png" class="deskButton" style="top:10px;display:none" onclick="exitButton()" />
- <img id="deskkeybutton3a" src="images/mobile-desk-menu-open.png" class="deskButton" style="top:60px;display:none" onclick="toggleMenu(false)" />
- <img id="deskkeybutton3b" src="images/mobile-desk-menu-close.png" class="deskButton" style="top:60px;display:none" onclick="toggleMenu(true)" />
- <img id="deskkeybutton4a" src="images/mobile-desk-mouse-left.png" class="deskButton" style="top:110px;display:none" onclick="deskChangeMouseButton(0)" />
- <img id="deskkeybutton4b" src="images/mobile-desk-mouse-right.png" class="deskButton" style="top:110px;display:none" onclick="deskChangeMouseButton(1)" />
- <img id="deskkeybutton5a" src="images/mobile-desk-scale-out.png" class="deskButton" style="top:160px;display:none" onclick="deskChangeFullscreenZoom()" />
- <img id="deskkeybutton5b" src="images/mobile-desk-scale-in.png" class="deskButton" style="top:160px;display:none" onclick="deskChangeFullscreenZoom()" />
- <img id="deskkeybutton2a" src="images/mobile-desk-keyboard-open.png" class="deskButton" style="top:210px;display:none" onclick="toggleKeyboard()" />
- <img id="deskkeybutton2b" src="images/mobile-desk-keyboard-close.png" class="deskButton" style="top:210px;display:none" onclick="toggleKeyboard()" />
- <div style="position:absolute;top:0;left:0;z-index:200;opacity:0;width:1px;height:1px">
- <input id="softKeyboard" autocapitalize="off" autocomplete="off" type="text" inputmode="text" spellcheck="false" style="z-index:200;opacity:0;width:1px;height:1px" onfocus="keyboardFocusChange()" onblur="keyboardFocusChange()" />
- </div>
- <div id="deskButtonMenu" style="display:none;position:absolute;top:10px;left:10px;right:55px;bottom:10px;z-index:1000"></div>
- <div id=p10desktop style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%;display:none">
- <div id=deskarea1 style="position:absolute;top:0px;width:100%;height:32px">
- <div style="padding-top:2px;padding-bottom:2px;background:#C0C0C0;height:32px">
- <div style="float:right;text-align:right">
- <span id="p14power"></span>
- <input type=button id=deskFullScreen value="Full Screen" onclick=deskToggleFull(event) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px;margin-right:3px;">
- </div>
- <div style="margin-left:3px">
- <input type=button id=connectbutton1 value="Connect" onclick=connectDesktop(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px">
- <input type=button id=connectbutton1h value="HW Connect" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px">
- <input type=button id=disconnectbutton1 value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" style="height:28px">
- <span id="deskstatus" style="color:black">Disconnected</span>
- </div>
- </div>
- </div>
- <div id=deskarea3 style="position:absolute;top:32px;width:100%;height:calc(100% - 64px);background-color:#000;text-align:center">
- <div id=DeskParent style="height:100%">
- <canvas id=Desk width=640 height=200 style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
- </div>
- <div id=p11DeskConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px;text-align:left" onclick=p11clearConsoleMsg()></div>
- <div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div>
- </div>
- <div id=deskarea4 style="position:absolute;bottom:0px;width:100%;height:32px">
- <div style=padding-top:2px;padding-bottom:2px;background:#C0C0C0>
- <div style=float:right;text-align:right;padding-right:2px>
- <span id=DeskLockButton><img src='images/icon-lock.png' onclick=deviceLockFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
- <span id=DeskChatButton><img src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
- <span id=DeskToastButton><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
- <span id=DeskOpenWebButton><img src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
- <span id=DeskRunButton><img src='images/icon-play.png' onclick=runDeviceCmd() height=16 width=16 style=padding-top:5px;cursor:pointer /></span>
- <!--<input id=DeskToolsButton type=button value=Tools onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()"> -->
- </div>
- <div>
- <input id="deskActionsBtn" type=button style="margin-left:3px;height:28px" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
- <input type="button" value="Settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" style="height:28px">
- <input type="button" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none;height:28px">
- <!--<input type="button" id="DeskSpecialKeys" value="Keys" onkeypress="return false" onkeydown="return false" onclick="sendSpecialKeys()" style="height:28px">-->
- <input type="button" id="DeskScreens" value="Screens" onkeypress="return false" onkeydown="return false" onclick="deskSelectScreens()" style="display:none;height:28px">
- <label><span id="DeskControlSpan" style="display:none"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false">Input</span></label>
- </div>
- </div>
- </div>
- </div>
- <div id="termButtonMenu" style="display:none;position:absolute;top:10px;left:10px;right:55px;bottom:10px;z-index:1000"></div>
- <div id=p10terminal style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%;display:none;background-color:#333">
- <div id=termTable style="position:absolute;top:0;bottom:0;left:0;right:0">
- <div id="termarea1">
- <div class="areaHead" style="line-height:24px">
- <div class="toright2">
- <input type=button id=termFullScreen value="Full Screen" onclick=deskToggleFull(event) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px;margin-right:3px;">
- <div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div>
- </div>
- <div>
- <span id="connectbutton2span" style="margin-left:3px"><input type="button" id="connectbutton2" value="Connect" style="height:28px" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
- <span id="connectbutton2sspan" style="margin-right:4px"><input type="button" id="connectbutton2s" value="SSH Connect" style="height:28px" onclick=connectTerminal(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
- <span id="disconnectbutton2span" style="margin-left:3px"><input type="button" id="disconnectbutton2" value="Disconnect" style="height:28px" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
- <span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
- </div>
- </div>
- </div>
- <div id="termarea3" style="width:100%;height: calc(100% - 60px);" cellpadding=0 cellspacing=0>
- <div id="termarea3x" style="width:100%;height:100%">
- <div style="width:100%;height:100%;text-align:left" id="termarea3xdiv"></div>
- </div>
- </div>
- <div id="termarea4" style="position:relative;height:32px;">
- <div class="areaFoot">
- <div class="toright2"></div>
- <div style="height:28px">
- <input id="termActionsBtn" style="margin-left:3px;height:28px" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
- <input id="ctrlcbutton" style="margin-left:3px;height:28px" type=button onkeypress="return false" onkeydown="return false" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')" />
- <input id="ctrlxbutton" style="margin-left:3px;height:28px" type=button onkeypress="return false" onkeydown="return false" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')" />
- <input id="escbutton" style="margin-left:3px;height:28px" type=button onkeypress="return false" onkeydown="return false" value="ESC" onclick="termSendKey(27,'escbutton')" />
- </div>
- </div>
- </div>
- <div id=p12TermConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
- </div>
- </div>
- <div id=p10files style="position:absolute;top:55px;bottom:0px;width:100%;display:none">
- <table id="p13toolbar" style="width:100%;height:111px" cellpadding="0" cellspacing="0">
- <tr>
- <td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px;line-height:24px">
- <div style="float:right;text-align:right">
- <input id="filesActionsBtn" type=button onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() style=margin-right:2px />
- <div id="filesCustomUpperRight" style="float:left;margin-right:6px"></div>
- </div>
- <div style="margin-left:2px">
- <input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) onkeypress="return false" onkeydown="return false" type="button" style="display:none">
- <input id=p13Connect value="Connect" onclick=connectFiles(event,1) onkeypress="return false" onkeydown="return false" type="button" />
- <input id=p13Connects value="SFTP Connect" onclick=connectFiles(event,2) onkeypress="return false" onkeydown="return false" type="button" />
- <input id=p13Disconnect value="Disconnect" onclick=connectFiles(event) onkeypress="return false" onkeydown="return false" type="button" />
- <span class=textOverGray id=p13Status>Disconnected</span>
- </div>
- </td>
- </tr>
- <tr>
- <td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom>
- <div style="width:100%;text-align:center">
- <input type=button style="width:calc(100%/5 - 5px)" id=p13FolderUp disabled="disabled" onclick="p13folderup()" value="Up" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13SelectAllButton disabled="disabled" onclick="p13selectallfile()" value="SelectAll" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13RenameFileButton disabled="disabled" value="Rename" onclick="p13renamefile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13DeleteFileButton disabled="disabled" value="Delete" onclick="p13deletefile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13NewFolderButton disabled="disabled" value="Folder" onclick="p13createfolder()" onkeypress="return false" onkeydown="return false" />
- </div>
- <div style="width:100%;text-align:center">
- <input type=button style="width:calc(100%/5 - 5px)" id=p13UploadButton disabled="disabled" value="Upload" onclick="p13uploadFile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13CutButton disabled="disabled" value="Cut" onclick="p13copyFile(1)" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13CopyButton disabled="disabled" value="Copy" onclick="p13copyFile(0)" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13PasteButton disabled="disabled" value="Paste" onclick="p13pasteFile()" onkeypress="return false" onkeydown="return false" />
- <input type=button style="width:calc(100%/5 - 5px)" id=p13RefreshButton disabled="disabled" value="Refresh" onclick="p13folderup(9999)" onkeypress="return false" onkeydown="return false" />
- </div>
- </td>
- </tr>
- <tr>
- <td style="background-color:#E4E9E7;height:28px">
- <table style="width:100%">
- <tr>
- <td id=p13currentpath style="overflow:hidden;padding-left:4px;padding-top:2px;color:black"></td>
- <td style="text-align:right;padding-right:4px">
- <select id=p13sortdropdown onchange=p13updateFiles()>
- <option value=1 selected="selected">Sort by name</option>
- <option value=2>Sort by size</option>
- <option value=3>Sort by date</option>
- <option value=4>Descend by name</option>
- <option value=5>Descend by size</option>
- <option value=6>Descend by date</option>
- </select>
- </td>
- </tr>
- </table>
- </td>
- </tr>
- </table>
- <div id=p13FilesConsoleMsg style="display:none;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
- <div id="p13filetable" style="width:100%;height:calc(100% - 133px);overflow:auto;-webkit-user-select:none">
- <!--
- <div id="p13bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✓</b></div>
- <div id="p13bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✗</b></div>
- -->
- <span id="p13files"></span>
- </div>
- <table id="p13toolbarBottom" style="width:100%;height:22px;position:absolute;bottom:0px" cellpadding=0 cellspacing=0>
- <tr><td style="text-align:left;padding:3px;text-align:center;background-color:#D3D9D6;color:black"> <span id="p13bottomstatus"></span></td></tr>
- </table>
- </div>
- <div id=p10details style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
- <div id=p10detailshtml style="margin-left:-3px"></div>
- </div>
- <div id=p10console style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%">
- <table id="consoleTable" cellpadding=0 cellspacing=0>
- <tr style="height:28px">
- <td class="areaHead">
- <div class="toright2">
- <div id=p15coreName></div>
- <input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) />
- </div>
- <div id="p15statetext"></div>
- </td>
- </tr>
- <tr>
- <td id=p15agentConsole style="position:relative">
- <pre id=p15agentConsoleText></pre>
- </td>
- </tr>
- <tr style="height:28px">
- <td class="areaFoot">
- <table style="width:100%">
- <tr>
- <td style="width:99%">
- <input id=p15consoleText style=width:100%;box-sizing:border-box onkeyup=p15consoleSend(event) />
- </td>
- <td id="p15outputselecttd">
- <select id=p15outputselect onchange="setupConsole()">
- <option id="p15outputselect1" value=1>Agent</option>
- <option id="p15outputselect3" value=3>Push</option>
- <option id="p15outputselect2" value=2>MQTT</option>
- </select>
- </td>
- <td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td>
- </tr>
- </table>
- </td>
- </tr>
- </table>
- </div>
- </div>
- <div id=p20 style="display:none;position:absolute;bottom:0;top:0;width:100%">
- <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0">
- <tr style=padding:0>
- <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()>
- <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0">
- <div class="menucurve"></div>
- </div>
- <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div>
- </td>
- <td onclick="p20editmesh(1)">
- <img src="/images/meshicon50.png" width=50 height=50 />
- </td>
- <td onclick="p20editmesh(1)">
- <div style=margin-left:5px>
- <strong style="font-size:large"><span id=p20meshName></span></strong><br />
- </div>
- </td>
- </tr>
- </table>
- <div style="overflow-y:auto;position:absolute;top:55px;bottom:0px;left:0px;right:0px">
- <div id=p20info style="margin-left:8px;margin-right:8px"></div>
- </div>
- </div>
- </div>
- </div>
- <div id=footer style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px">
- <table id=footerMenu cellpadding=0 cellspacing=0 style="height:32px;width:100%;color:white;cursor:pointer;table-layout:fixed"></table>
- </div>
- </div>
- <div id=dialog style="display:none">
- <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0">
- <div id=id_dialogclose style=float:right;padding:5px;cursor:pointer onclick=setDialogMode()><b>X</b></div>
- <div id=id_dialogtitle style=padding:5px></div>
- <div style=width:100%;margin:6px></div>
- </div>
- <div style="margin-right:16px;margin-left:8px">
- <div id=dialog1 style="margin:auto;text-align:center;margin:3px">
- <div id=id_dialogMessage style="padding:10px"></div>
- </div>
- <div id=dialog2 style="margin:auto;margin:3px">
- <div id=id_dialogOptions></div>
- </div>
- <div id=dialog3 style="margin:auto;margin:3px">
- <select id="deskkeys" style="width:100%">
- <option value=10>Ctrl+Alt+Del</option>
- <option value=11>Tab</option>
- <option value=5>Win</option>
- <option value=0>Win+Down</option>
- <option value=1>Win+Up</option>
- <option value=2>Win+L</option>
- <option value=3>Win+M</option>
- <option value=4>Shift+Win+M</option>
- <option value=6>Win+R</option>
- <option value=7>Alt-F4</option>
- <option value=8>Ctrl-W</option>
- <option value=9>Alt-Tab</option>
- <option value=12>Shift-F10</option>
- </select>
- </div>
- <div id=dialog4 style="margin:auto;margin:3px">
- <div id=d3upload>
- <div>File Selection</div>
- <select id=d3uploadMode onchange=d3modechange()>
- <option value=1>Local file upload</option>
- <option value=2>Server file selection</option>
- </select>
- </div>
- <div id=d3localmode style="display:none">
- <div>Upload File</div>
- <form id=d3localmodeform method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
- <input type=text id=d3auth name=auth style="display:none" />
- <input type=text id=d3filter name=filter style="display:none" />
- <input type=text id=d3attrib name=attrib style="display:none" />
- <input type=file id=d3localFile name=files onchange=d3setActions() />
- <input type=submit id=d3submit style="display:none" />
- </form>
- </div>
- <div id=d3servermode>
- <div id=d3serveraction valign=bottom>
- <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" /> <span id=p3CurrentFolder></span>
- </div>
- <div id=d3serverfiles></div>
- </div>
- </div>
- <div id=dialog7 style="margin:auto;margin:3px">
- <div id="d7meshkvm">
- <h4 style="width:100%;border-bottom:1px solid gray">Agent Remote Desktop</h4>
- <table style="width:100%">
- <tr>
- <td>
- Quality
- </td>
- <td style="width:100px">
- <select id="d7bitmapquality" style="float:right;width:200px" dir="rtl"></select>
- </td>
- </tr>
- <tr>
- <td>
- Scaling
- </td>
- <td style="width:100px">
- <select id="d7bitmapscaling" style="float:right;width:200px" dir="rtl">
- <option selected=selected value=1024>100%</option>
- <option value=896>87.5%</option>
- <option value=768>75%</option>
- <option value=640>62.5%</option>
- <option value=512>50%</option>
- <option value=384>37.5%</option>
- <option value=256>25%</option>
- <option value=128>12.5%</option>
- </select>
- </td>
- </tr>
- <tr>
- <td>
- Rate
- </td>
- <td style="width:100px">
- <select id="d7framelimiter" style="float:right;width:200px" dir="rtl">
- <option selected=selected value=50>Fast</option>
- <option value=100>Medium</option>
- <option value=400>Slow</option>
- <option value=1000>Very slow</option>
- </select>
- </td>
- </tr>
- <tr>
- <td>
- Encoding
- </td>
- <td style="width:100px">
- <select id="d7encoding" style="float:right;width:200px" dir="rtl">
- <option value=1>JPEG</option>
- <option value=2>PNG</option>
- <option value=3>TIFF</option>
- <option selected=selected value=4>WEBP</option>
- </select>
- </td>
- </tr>
- <tr>
- <td></td>
- <td>
- <label style="display:block" id="d7deskAutoLockLabel"><input type="checkbox" id="d7deskAutoLock" />Lock on Disconnect</label>
- </td>
- </tr>
- </table>
- </div>
- <div id="d7amtkvm">
- <h4 style="width:100%;border-bottom:1px solid gray">Intel® AMT Hardware KVM</h4>
- <table style="width:100%">
- <tr>
- <td>Encoding</td>
- <td style="width:100px">
- <select id="d7desktopmode" style="float:right;width:200px">
- <option value="1">RLE8, Fastest</option>
- <option value="2">RLE16, Recommended</option>
- <option value="3">RAW8, Slow</option>
- <option value="4">RAW16, Very Slow</option>
- </select>
- </td>
- </tr>
- </table>
- </div>
- </div>
- </div>
- <div id="idx_dlgButtonBar" style="padding:10px;margin-bottom:20px">
- <input id="idx_dlgCancelButton" type="button" value="Cancel" style="float:right;width:80px;margin-left:5px" onclick="dialogclose(0)">
- <input id="idx_dlgOkButton" type="button" value="OK" style="float:right;width:80px" onclick="dialogclose(1)">
- <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div>
- </div>
- </div>
- <div id=topMenu style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:0px 0px 5px 5px;position:fixed;top:50px;right:5px;width:170px;display:none">
- <div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(2)>My Files</div>
- <div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(1)>My Account</div>
- <div id="logoutMenuOption"><a id="logoutMenuOptionRef" href=/logout><div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer">Logout</div></a></div>
- </div>
- <audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio>
- <iframe name="fileUploadFrame" style=display:none></iframe>
- <script>
- 'use strict';
- var random = '{{{randomlength}}}' // Random length string for BREACH mitigation
- // Process server-side web state
- var webState = '{{{webstate}}}';
- if (webState != '') { webState = JSON.parse(decodeURIComponent(webState)); }
- for (var i in webState) { try { localStorage.setItem(i, webState[i]); } catch (ex) { } }
- if (webState && !webState.loctag) { try { delete localStorage.removeItem('loctag'); } catch (ex) { } }
- // Fetch URL arguments & do sanitation
- var urlargs = parseUriArgs();
- if (urlargs.key != null) { urlargs.key = "" + urlargs.key; }
- if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
- if (urlargs.locale && (isAlphaNumeric(urlargs.locale) == false)) { delete urlargs.locale; }
- delete urlargs.user;
- delete urlargs.pass;
- delete urlargs.viewmode;
- delete urlargs.gotonode;
- delete urlargs.gotodevicename;
- delete urlargs.gotodevicername;
- delete urlargs.gotodeviceip;
- delete urlargs.gotomesh;
- delete urlargs.panel;
- // Check if we are in debug mode
- var args = parseUriArgs();
- if (args.key && (isAlphaNumeric(args.key) == false)) { delete args.key; }
- if (args.locale && (isAlphaNumeric(args.locale) == false)) { delete args.locale; }
- var debugLevel = parseInt('{{{debuglevel}}}');
- var features = parseInt('{{{features}}}');
- var features2 = parseInt('{{{features2}}}');
- var sessionTime = parseInt('{{{sessiontime}}}');
- var sessionRefreshTimer = null;
- var domain = '{{{domain}}}';
- var domainUrl = '{{{domainurl}}}';
- var authCookie = '{{{authCookie}}}';
- var authRelayCookie = '{{{authRelayCookie}}}';
- var logoutControls = JSON.parse(decodeURIComponent('{{{logoutControls}}}'));
- var authCookieRenewTimer = null;
- var webRelayPort = parseInt('{{{webRelayPort}}}');
- var hidePowerTimeline = '{{{hidePowerTimeline}}}';
- var webRelayDns = '{{{webRelayDns}}}';
- var meshserver = null;
- var xdr = null;
- var usergroups = null;
- var stars = {}; // Devices that have been "stared" by the user.
- var serverinfo = null;
- var nodes = [];
- var meshes = {};
- var filetree = {};
- var userinfo = null;
- var serverinfo = null;
- var users = null;
- var nodeShortIdent = 0;
- var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
- var debugmode = false;
- var attemptWebRTC = ((features & 128) != 0);
- var webrtcconfiguration = '{{{webrtcconfig}}}';
- if (webrtcconfiguration == '') { webrtcconfiguration = null; } else { try { webrtcconfiguration = JSON.parse(decodeURIComponent(webrtcconfiguration)); } catch (ex) { console.log('Invalid WebRTC config: "' + webrtcconfiguration + '".'); webrtcconfiguration = null; } }
- var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel® AMT Connected"];
- var agentsStr = ["Unknown", "Windows 32bit console", "Windows 64bit console", "Windows 32bit service", "Windows 64bit service", "Linux 32bit", "Linux 64bit", "MIPS", "XENx86", "Android", "Linux ARM", "macOS x86-32bit", "Android x86", "PogoPlug ARM", "Android", "Linux Poky x86-32bit", "macOS x86-64bit", "ChromeOS", "Linux Poky x86-64bit", "Linux NoKVM x86-32bit", "Linux NoKVM x86-64bit", "Windows MinCore console", "Windows MinCore service", "NodeJS", "ARM-Linaro", "ARMv6l / ARMv7l", "ARMv8 64bit", "ARMv6l / ARMv7l / NoKVM", "MIPS24KC (OpenWRT)", "Apple Silicon", "FreeBSD x86-64", "Unknown", "Linux ARM 64 bit (glibc/2.24 NOKVM)", "Alpine Linux x86 64 Bit (MUSL)", "Assistant (Windows)", "Armada370 - ARM32/HF (libc/2.26)", "OpenWRT x86-64", "OpenBSD x86-64", "Unknown", "Unknown", "MIPSEL24KC (OpenWRT)", "ARMADA/CORTEX-A53/MUSL (OpenWRT)", "Windows ARM 64bit console", "Windows ARM 64bit service", "ARMVIRT32 (OpenWRT)", "RISC-V x86-64"];
- var files;
- var terminal;
- var passRequirements = '{{{passRequirements}}}';
- if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
- var sessionActivity = Date.now();
- var deskPinchZoom;
- var deskKeyboardShortcuts = [];
- var nightMode = setNightMode();
- var xterm = null;
- var xtermfit = null;
- var xtermResizeTimer = null;
- var devicePagingState = null;
- // Console Message Display Timers
- var p11DeskConsoleMsgTimer = null;
- var p12TermConsoleMsgTimer = null;
- var p13FilesConsoleMsgTimer = null;
- // Check if WebP is supported
- var webpSupport = false;
- check_webp_feature('lossy', function (f, x) {
- webpSupport = x;
- if (!x) {
- d7encoding.options[1].disabled = true;
- d7encoding.value = 1;
- }
- });
- function startup() {
- if ((features & 32) == 0) {
- // Guard against other site's top frames (web bugs).
- var loc = null;
- try { loc = top.location.toString().toLowerCase(); } catch (e) { }
- if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
- }
- if (!args.locale) { var x = getstore('loctag', 0); if ((x != null) && (x != '*')) { args.locale = x; } }
- window.onresize = center;
- center();
- QV('changeEmailId', (features & 0x200000) == 0);
- QH('p1message', "Connecting...");
- go(1);
- // Document keys
- document.onkeypress = ondeskkeypress;
- document.onkeydown = ondeskkeydown;
- document.onkeyup = ondeskkeyup;
- document.onclick = function (e) { if ((xxdialogMode == 999) && (e.target.id != 'topMenuIcon')) { QV('topMenu', false); xxdialogMode = 0; } }
- // Connect to the mesh server
- meshserver = MeshServerCreateControl(domainUrl);
- meshserver.onStateChanged = onStateChanged;
- meshserver.onMessage = onMessage;
- meshserver.trace = args.trace;
- meshserver.Start();
- // Setup stared devices
- try { stars = JSON.parse(getstore('stars', '{}')); } catch (ex) { }
- // Setup logout control
- if (logoutControls && logoutControls.logoutUrl) { Q('logoutMenuOptionRef').href = logoutControls.logoutUrl; }
- // Load desktop settings
- var t = localStorage.getItem('desktopsettings');
- if (t != null) { desktopsettings = JSON.parse(t); }
- applyDesktopSettings();
- //attemptWebRTC = false; // For now, default WebRTC off unless we set it in the URL.
- if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
- // Session Refresh Timer
- if (sessionTime >= 10) { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); }
- // Hide night mode button if needed
- QV('setDarkModeLink', (features2 & 0x00300000) == 0);
- // Set the user's desktop shortcut keys
- deskKeyboardShortcuts = [];
- var deskKeyboardShortcutsStr = getstore('deskKeyShortcuts', '0x0A002E,0x100000,0x100028,0x100026,0x10004C,0x10004D,0x11004D,0x100052,0x020073,0x080057,0x020009,0x100025,0x100027').split(',');
- for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); }
- updateDeskShortcutKeys();
- updateTermShortcutKeys();
- }
- function refreshCookieSession() {
- var xdr = null;
- try { xdr = new XDomainRequest(); } catch (e) { }
- if (!xdr) xdr = new XMLHttpRequest();
- xdr.open('GET', window.location.origin + domainUrl + 'refresh.ashx');
- xdr.timeout = 15000;
- xdr.onload = function () { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); };
- xdr.onerror = xdr.ontimeout = function () { sessionRefreshTimer = null; };
- xdr.send();
- }
- function onStateChanged(server, state, prevState, errorCode) {
- if (state == 0) {
- // Control web socket disconnected
- setDialogMode(0); // Close any dialog boxes if present
- go(0); // Go to disconnection panel
- deleteAllNotifications(); // Close and clear notifications if present
- if (errorCode == 'noauth') { QH('p0span', "Unable to perform authentication"); return; }
- if (prevState == 2) { setTimeout(serverPoll, 5000); } else { QH('p0span', "Unable to connect web socket"); }
- // Clean up here
- if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
- devicePagingState = null;
- updateDevicePageState();
- } else if (state == 2) {
- // Fetch list of meshes, nodes, files
- meshserver.send({ action: 'usergroups' });
- meshserver.send({ action: 'meshes' });
- meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
- meshserver.send({ action: 'files' });
- authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
- }
- QV('topMenuIcon', state == 2);
- }
- // Poll the server, if it responds, refresh the page.
- function serverPoll() {
- xdr = null;
- try { xdr = new XDomainRequest(); } catch (e) { }
- if (!xdr) xdr = new XMLHttpRequest();
- xdr.open('HEAD', window.location.href);
- xdr.timeout = 15000;
- // Make sure there isn't a reverse proxy in front that may just be returning 5xx codes
- // Status code 4xx should still be allowed, since a page could potentially be removed, etc
- xdr.onload = function () { if (xdr.status < 500) reload(); else setTimeout(serverPoll, 10000); };
- xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); };
- xdr.send();
- }
- function updateSelf() {
- var accountSettingsLocked = ((features2 & 0x100) != 0);
- if (userinfo) { accountSettingsLocked = ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 1024) != 0)) || ((features2 & 0x100) != 0); } // Not admin and have account features locked, or using a loginToken
- QV('p3AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (accountSettingsLocked == false)); // Hide Account Actions if in single user mode or domain authentication
- QV('logoutMenuOption', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide logout if in single user mode or domain authentication
- QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0) && (accountSettingsLocked == false)); // Hide Account Security if in single user mode or domain authentication, 2 factor auth not supported.
- QV('p2AccountImage', !accountSettingsLocked);
- QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
- QV('manageAuthApp', (serverinfo.lock2factor != true) && (features & 4096) && ((userinfo.otpsecret == 1) || ((features2 & 0x00020000) == 0)));
- QV('manageOtp', (serverinfo.lock2factor != true) && ((features2 & 0x40000) == 0) && (features & 4096) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)));
- QV('authPhoneNumberCheck', (userinfo.phone != null));
- QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
- QV('authAppSetupCheck', userinfo.otpsecret == 1);
- //QV('authKeySetupCheck', userinfo.otphkeys > 0);
- QV('authCodesSetupCheck', userinfo.otpkeys > 0);
- QV('p2AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null));
- QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null) && (userinfo._id.split('/')[2].startsWith('~') == false)); // Hide Account Actions if in single user mode or domain authentication
- // On the mobile app, don't allow group creation (for now).
- QV('p3createMeshLink1', false);
- QV('p3createMeshLink2', false);
- // Update user image
- if ((userinfo.flags != null) && (userinfo.flags & 1)) {
- if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
- Q('p2AccountImage').src = 'userimage.ashx?rnd=' + userinfo.accountImageRnd;
- } else {
- Q('p2AccountImage').src = 'images/user-256.png';
- }
- if (typeof userinfo.passchange == 'number') {
- if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
- else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
- var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
- if (seconds < 0) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
- else if (seconds < 3600) { var secs = Math.floor(seconds / 60); QH('p2nextPasswordUpdateTime',format((secs == 1)?" - Reset in 1 minute.":" - Reset in {0} minutes.", secs)); }
- else if (seconds < 86400) { var hours = Math.floor(seconds / 3600); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 hour." : " - Reset in {0} hours.", hours)); }
- else { var days = Math.floor(seconds / 86400); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 day." : " - Reset in {0} days.", days)); }
- }
- }
- }
- function setSessionActivity() { sessionActivity = Date.now(); }
- function checkIdleSessionTimeout() {
- var delta = (Date.now() - sessionActivity);
- if (delta > serverinfo.timeout) {
- if (desktop != null) { // Disconnect remote desktop
- desktop.Stop();
- desktopNode = desktop = null;
- }
- if (terminal != null) { // Disconnect terminal
- terminal.Stop();
- terminal = null;
- }
- if (files != null) { // Disconnect files
- files.Stop();
- files = null;
- }
- if (serverinfo.logoutonidlesessiontimeout) {
- if (urlargs.key) { window.location.href = 'logout?key=' + urlargs.key; }
- else { window.location.href = 'logout'; }
- }
- }
- }
- function onMessage(server, message) {
- switch (message.action) {
- case 'serverinfo': {
- serverinfo = message.serverinfo;
- if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
- if (userinfo != null) updateSelf();
- if (serverinfo.certExpire != null) {
- var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000);
- if ((days >= 0) && (days < 20)) {
- addNotification({ text: format("Certificate expires in {0} day(s)", days) });
- }
- }
- // Arrange the user interface
- QV('manageEmail2FA', (features & 0x00800000) && (serverinfo.lock2factor != true));
- QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000) && (serverinfo.lock2factor != true));
- QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000) && (serverinfo.lock2factor != true));
- break;
- }
- case 'authcookie': {
- // Got an authentication cookie refresh
- authCookie = message.cookie;
- authRelayCookie = message.rcookie;
- break;
- }
- case 'userinfo': {
- userinfo = message.userinfo;
- QH('p3userName', userinfo.name);
- //updateSiteAdmin();
- if (serverinfo != null) updateSelf();
- break;
- }
- case 'users': {
- users = {};
- for (var m in message.users) { users[message.users[m]._id] = message.users[m]; }
- if (currentUser != null) { currentUser = users[currentUser._id]; }
- updateUsers();
- break;
- }
- case 'wssessioncount': {
- wssessions = message.wssessions;
- updateUsers();
- break;
- }
- case 'meshes': {
- meshes = {};
- for (var m in message.meshes) { meshes[message.meshes[m]._id] = message.meshes[m]; }
- if (currentMesh != null) { currentMesh = meshes[currentMesh._id]; }
- updateMeshes();
- mainUpdate(4);
- break;
- }
- case 'usergroups': {
- var groupCount = 0;
- if (Array.isArray(message.ugroups)) {
- usergroups = {};
- for (var i in message.ugroups) { groupCount++; usergroups[message.ugroups[i]._id] = message.ugroups[i]; }
- if (groupCount == 0) { usergroups = null; }
- } else {
- usergroups = message.ugroups;
- for (var i in message.ugroups) { groupCount++; }
- if (groupCount == 0) { usergroups = null; }
- }
- //mainUpdate(8192);
- break;
- }
- case 'files': {
- filetree = setupBackPointers(message.filetree);
- updateFiles();
- //d3updatefiles();
- break;
- }
- case 'nodes': {
- nodes = [];
- for (var m in message.nodes) {
- for (var n in message.nodes[m]) {
- message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase();
- if (message.nodes[m][n].rname) { message.nodes[m][n].rnamel = message.nodes[m][n].rname.toLowerCase(); } else { message.nodes[m][n].rnamel = message.nodes[m][n].namel; }
- message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*';
- message.nodes[m][n].meshid = m;
- message.nodes[m][n].state = (message.nodes[m][n].state) ? (message.nodes[m][n].state) : 0;
- message.nodes[m][n].desc = message.nodes[m][n].desc;
- if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1;
- message.nodes[m][n].ident = ++nodeShortIdent;
- nodes.push(message.nodes[m][n]);
- }
- }
- // If we are currently looking at a node this is now gone, change the view.
- if ((currentNode != null) && (IsNodeViewable(currentNode) == false)) { currentNode = null; go(2); }
- // Change the reference to the current node
- if (currentNode != null) { currentNode = getNodeFromId(currentNode._id); if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); } else { go(2); } }
- // Update device paging
- devicePagingState = (message.totalcount == null) ? null : { total: message.totalcount, skip: message.skip, limit: message.limit };
- updateDevicePageState();
- //onSortSelectChange();
- //onSearchInputChanged();
- mainUpdate(4);
- //refreshMap(false, true);
- if (xxcurrentView == 0) { if ('{{viewmode}}' != '') { go(parseInt('{{viewmode}}')); } else { setDialogMode(0); go(2); } }
- if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}', parseInt('{{viewmode}}')); }
- break;
- }
- case 'powertimeline': {
- if (message.nodeid != powerTimelineReq) break;
- powerTimelineNode = message.nodeid;
- powerTimeline = message.timeline;
- powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes
- for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time
- if (currentNode._id == message.nodeid) { drawDeviceTimeline(); }
- break;
- }
- case 'getsysinfo': {
- if (message.nodeid != powerTimelineReq) break;
- if (message.noinfo === true) {
- updateDeviceDetails(getNodeFromId(message.nodeid));
- } else {
- message.hardware.time = message.time;
- updateDeviceDetails(getNodeFromId(message.nodeid), message.hardware);
- }
- break;
- }
- case 'lastconnect': {
- var node = getNodeFromId(message.nodeid);
- if (node != null) {
- node.lastconnect = message.time;
- node.lastaddr = message.addr;
- }
- break;
- }
- case 'msg': {
- // Check if this is a message from a node
- if (message.nodeid != null) {
- var index = -1;
- if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
- if (index != -1) {
- if (message.type == 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message.
- else if (message.type == 'notify') { // This is a notification message.
- var n = getstore('notifications', 0);
- if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored.
- var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
- if (message.id != null) { n.id = message.id; }
- if (message.nodeid != null) { n.nodeid = message.nodeid; }
- if (message.tag != null) { n.tag = message.tag; }
- if (message.url != null) { n.url = message.url; }
- if (message.username != null) { n.username = message.username; }
- if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
- addNotification(n);
- } else if ((message.type == 'userSessions') && (currentNode != null) && (currentNode._id == message.nodeid) && (desktop == null)) {
- // Got list of user sessions
- var userSessions = [];
- if (message.data != null) { for (var i in message.data) { if ((message.data[i].State == 'Active') || (message.data[i].StationName == 'Console') || (debugmode == 3)) { userSessions.push(message.data[i]); } } }
- if (userSessions.length == 0) { connectDesktop(null, 1, null, message.tag); } // No active sessions, do a normal connection.
- else if (userSessions.length == 1) { connectDesktop(null, 1, userSessions[0].SessionId, message.tag); } // One active session, connect to it
- else {
- var x = '';
- var sortBy = "{{{userSessionsSort}}}";
- if (sortBy != '') {
- userSessions.sort(function(a, b) {
- if (!a[sortBy]) return -1; // a comes before b
- if (!b[sortBy]) return 1; // b comes before a
- if (a[sortBy] < b[sortBy]) return -1;
- if (a[sortBy] > b[sortBy]) return 1;
- return 0;
- });
- }
- for (var i in userSessions) {
- x += '<div style="text-align:left;cursor:pointer;background-color:gray;margin:5px;padding:5px;border-radius:5px" onclick=connectDesktop(event,1,' + userSessions[i].SessionId + ',' + message.tag + ')>' + userSessions[i].State + ', ' + userSessions[i].StationName;
- if (userSessions[i].Username) { if (userSessions[i].Domain) { x += ' - ' + userSessions[i].Domain + '/' + userSessions[i].Username; } else { x += ' - ' + userSessions[i].Username; } }
- x += '</div>';
- }
- QH('p11DeskSessionSelector', x);
- QV('p11DeskSessionSelector', true);
- }
- }
- }
- } else {
- if (message.type == 'notify') { // This is a notification message.
- var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
- if (message.id != null) { n.id = message.id; }
- if (message.tag != null) { n.tag = message.tag; }
- if (message.url != null) { n.url = message.url; }
- if (message.username != null) { n.username = message.username; }
- if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
- addNotification(n);
- }
- }
- break;
- }
- case 'getnetworkinfo': {
- if (currentNode._id != message.nodeid) return;
- updateDeviceDetails(getNodeFromId(message.nodeid), null, message);
- break;
- }
- case 'getNotes': {
- var n = Q('d2devNotes');
- if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) {
- if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
- var ro = (n.attributes['ro'].value == 'true');
- if (ro == false) { // If we have permissions, set read/write on this note.
- n.removeAttribute('readonly');
- QE('idx_dlgOkButton', true);
- QV('idx_dlgOkButton', true);
- focusTextBox('d2devNotes');
- }
- }
- break;
- }
- case 'otpauth-request': {
- if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) {
- if (message.err != null) {
- var otpauthErrors = ['', "2FA is locked", "Backup codes are locked", "Login token in use", "OTP 2FA not allowed", "Account is locked", "Unable to load OTPLIB"];
- if ((message.err > 0) && (message.err < otpauthErrors.length)) { QH('d2optinfo', otpauthErrors[message.err]); } else { QH('d2optinfo', format("Error #{0}", message.err)); }
- } else {
- var secret = message.secret;
- if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
- else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
- QH('d2optinfo', format("Install" + ' <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" rel="noreferrer noopener" target=_blank>' + "Google Authenticator" + '</a> ' + "or a compatible application, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank> this link</a> or enter the secret below. Then, enter the current 6 digit token to activate 2-Step login.", message.url) + '<br /><br /><div style=width:100%;text-align:center><tt id=d2optsecret secret="' + message.secret + '" style=font-size:15px>' + secret + '</tt><br /><br />Token: <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></div>');
- QV('idx_dlgOkButton', true);
- QE('idx_dlgOkButton', false);
- Q('d2otpauthinput').focus();
- }
- }
- break;
- }
- case 'otpauth-setup': {
- if (xxdialogMode) return;
- setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b style=color:green>' + "Authenticator app activation successful." + '</b> ' + "You will now need a valid token to login again.") : ('<b style=color:red>' + "2-step login activation failed." + '</b> ' + "Clear the secret from the application and try again. You only have a few minutes to enter the proper code."));
- break;
- }
- case 'otpauth-clear': {
- if (xxdialogMode) return;
- setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b>' + "Authenticator application removed." + '</b> ' + "You can reactivate this feature at any time.") : ('<b style=color:red>' + "2-step login activation removal failed." + '</b> ' + "Try again."));
- break;
- }
- case 'otpauth-getpasswords': {
- if (xxdialogMode) return;
- var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place.";
- x += '<div style=\'border-radius:6px;border: 2px dashed #888;width:100%;margin-top:8px\'><div style=\'padding:8px;font-family:Arial, Helvetica, sans-serif;font-size:20px;font-weight:bold\'><table style=width:100%;text-align:center>';
- if (message.passwords) {
- var j = 0;
- for (var i in message.passwords) {
- if (++j % 2) { x += '<tr>'; }
- var p = '' + message.passwords[i].p;
- while (p.length < 8) { p = '0' + p; }
- if (message.passwords[i].u === true) { x += '<td>' + p.substring(0, 4) + ' ' + p.substring(4); } else { x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + ' ' + p.substring(4); + '</strike>'; }
- }
- } else {
- x += '<tr><td>' + "No Active Tokens";
- }
- x += '</table></div></div><br />';
- x += '<div><input type=button value=\'' + "Close" + '\' onclick=setDialogMode(0) style=float:right></input>';
- x += '<input type=button value=\'' + "New Tokens" + '\' onclick=\'account_manageOtp(1);\'></input>';
- if (message.passwords != null) { x += '<input type=button value=\'' + "Clear" + '\' onclick=\'account_manageOtp(2);\'></input>'; }
- x += '</div><br />';
- setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
- break;
- }
- case 'verifyPhone': {
- if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return;
- var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>';
- x += '<td>Check your phone and enter the verification code.';
- x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
- setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie);
- Q('d2phoneCodeInput').focus();
- account_managePhoneCodeValidate();
- break;
- }
- case 'previousLogins': {
- if ((xxdialogMode == 2) && (xxdialogTag == 'previousLogins')) {
- var x = '', c = 'BBB', xx = '';
- if (message.events.length == 0) {
- x += 'No previous login.';
- } else {
- x += '<div style=max-height:260px;overflow-y:scroll;overflow-x:hidden>';
- for (var i in message.events) {
- var m = message.events[i].m;
- if (m == 107) { m = "Valid login"; c = 'BBD1BB'; xx = ''; }
- else if (m == 108) { m = "Invalid 2FA"; c = 'DD9DC3'; xx = 'x'; }
- else if (m == 109) { m = "Locked account"; c = 'E1BBBB'; xx = 'x'; }
- else if (m == 110) { m = "Invalid password"; c = 'E1BBBB'; xx = 'x'; }
- x += '<div style=width:260px;background-color:#' + c + ';border-radius:6px;margin-bottom:4px;padding:4px><div><b>' + EscapeHtml(m) + '</b><br />' + printDateTime(new Date(message.events[i].t)) + '</div><div style=font-size:x-small>' + EscapeHtml(message.events[i].a.join(', ')) + '</div></div></tr>';
- }
- x += '</div>';
- }
- setDialogMode(2, "Previous Logins", 1, null, x);
- }
- break;
- }
- case 'event': {
- /*
- if (!message.event.nolog) {
- events.unshift(message.event);
- var eventLimit = parseInt(p3limitdropdown.value);
- while (events.length > eventLimit) { events.pop(); } // Remove element(s) at the end
- events_update();
- }
- */
- if (message.event.noact) break; // Take no action on this event
- switch (message.event.action) {
- case 'serverinfochange': {
- if (message.event.lock2factor != null) { serverinfo.lock2factor = message.event.lock2factor; updateSelf(); }
- break;
- }
- case 'userWebState': {
- // New user web state, update the web page as needed
- if (localStorage != null) {
- var webstate = JSON.parse(message.event.state);
- for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
- // Update stars
- if (webstate.stars != null) { stars = JSON.parse(webstate.stars); }
- // Update the web page
- if ((webstate.loctag != null) && (webstate.loctag != oldLoctag)) {
- if (webstate.loctag != null) { args.locale = webstate.loctag; } else { delete args.locale; }
- mainUpdate(4 + 128);
- } else if (webstate.stars != null) {
- mainUpdate(4);
- if (Q('SearchInput').value == '*') { onSearchInputChanged(); }
- }
- if (currentNode) { refreshDevice(currentNode._id); }
- // Set the user's desktop shortcut keys
- if (webstate.deskKeyShortcuts != null) {
- deskKeyboardShortcuts = [];
- var deskKeyboardShortcutsStr = webstate.deskKeyShortcuts.split(',');
- for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); }
- updateDeskShortcutKeys();
- }
- }
- break;
- }
- case 'accountchange': {
- // An account was created or changed
- if ((typeof message.event.account != 'object') || (message.event.account == null)) { console.log(message.event); return; };
- if (userinfo.name == message.event.account.name) {
- var newsiteadmin = message.event.account.siteadmin ? message.event.account.siteadmin : 0;
- var oldsiteadmin = userinfo.siteadmin ? userinfo.siteadmin : 0;
- if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); }
- userinfo = message.event.account;
- //if (oldsiteadmin != newsiteadmin) updateSiteAdmin();
- updateSelf();
- // If our list of nodes may have changes, request the new list now.
- if (message.event.nodeListChange == userinfo._id) { meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip }); }
- }
- break;
- }
- case 'createusergroup':
- case 'usergroupchange': {
- // User group changed
- if (usergroups == null) { usergroups = {}; }
- var ugroup = usergroups[message.event.ugrpid];
- if (ugroup == null) {
- // This is a new user group for us
- usergroups[message.event.ugrpid] = { _id: message.event.ugrpid, name: message.event.name, desc: message.event.desc, domain: message.event.domain, links: message.event.links };
- } else {
- // This is an existing user group
- ugroup.name = message.event.name;
- ugroup.desc = message.event.desc;
- ugroup.links = message.event.links;
- ugroup.flags = message.event.flags;
- }
- //mainUpdate(8192 + 16384);
- // Group update, refresh all our device groups and nodes. TODO: Optimize this to only do this when needed.
- meshserver.send({ action: 'meshes' });
- meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
- break;
- }
- case 'deleteusergroup': {
- // User group removed
- if ((usergroups != null) && (usergroups[message.event.ugrpid] != null)) {
- delete usergroups[message.event.ugrpid];
- var c = 0;
- for (var i in usergroups) { c++; }
- if (c == 0) { usergroups = null; } // If user groups is empty, set it to null.
- //mainUpdate(8192 + 16384);
- }
- break;
- }
- case 'createmesh': {
- // A new mesh was created
- if ((meshes[message.event.meshid] == null) && ((userinfo.manageAllDeviceGroups) || (message.event.mesh.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
- meshes[message.event.meshid] = message.event.mesh;
- mainUpdate(4 + 128);
- meshserver.send({ action: 'files' });
- }
- break;
- }
- case 'meshchange': {
- // Update mesh information
- if (meshes[message.event.meshid] == null) {
- // Check if we have any access to this device group
- var add = false;
- if (message.event.links[userinfo._id] != null) { add = true; }
- if (userinfo.links[message.event.meshid] != null) { add = true; }
- for (var i in userinfo.links) { if ((i.startsWith('ugrp/')) && (message.event.links[i] != null)) { add = true; } }
- // This is a new mesh for us
- if (add) {
- meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links, relayid: message.event.relayid };
- meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip }); // Request a refresh of all nodes (TODO: We could optimize this to only request nodes for the new mesh).
- }
- } else {
- // This is an existing mesh
- if (meshes[message.event.meshid].name != message.event.name) {
- meshes[message.event.meshid].name = message.event.name;
- for (var i in nodes) { if (nodes[i].meshid == message.event.meshid) { nodes[i].meshnamel = message.event.name.toLowerCase(); } }
- }
- meshes[message.event.meshid].desc = message.event.desc;
- meshes[message.event.meshid].links = message.event.links;
- if (message.event.relayid != null) { meshes[message.event.meshid].relayid = message.event.relayid; }
- // Check if we lost rights to this mesh in this change.
- if (IsMeshViewable(message.event.meshid) == false) {
- if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
- delete meshes[message.event.meshid];
- // Delete all nodes in that mesh, except ones with direct links
- var newnodes = [];
- for (var i in nodes) { if ((nodes[i].meshid != message.event.meshid) || ((userinfo.links != null) && (userinfo.links[nodes[i]._id] != null))) { newnodes.push(nodes[i]); } }
- nodes = newnodes;
- // If we are looking at a node in the deleted mesh, move back to "My Devices"
- if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); currentNode = null; }
- }
- }
- mainUpdate(4 + 128);
- meshserver.send({ action: 'files' });
- // If we are looking at a mesh that is now deleted, move back to "My Account"
- if (xxcurrentView == 20 && currentMesh._id == message.event.meshid) { p20updateMesh(); }
- break;
- }
- case 'deletemesh': {
- // Delete the mesh
- if (meshes[message.event.meshid]) {
- delete meshes[message.event.meshid];
- updateMeshes();
- meshserver.send({ action: 'files' });
- }
- // Delete all nodes in that mesh
- var newnodes = [];
- for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } }
- nodes = newnodes;
- mainUpdate(4);
- // If we are looking at a mesh that is now deleted, move back to "My Account"
- if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); }
- // If we are looking at a node in the deleted mesh, move back to "My Devices"
- if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); }
- break;
- }
- case 'addnode': {
- var node = message.event.node;
- if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
- if (getNodeFromId(node._id) != null) break; // This node is already known.
- node.namel = node.name.toLowerCase();
- if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
- node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
- node.state = 0;
- if (!node.icon) node.icon = 1;
- node.ident = ++nodeShortIdent;
- nodes.push(node);
- //onSortSelectChange();
- //onSearchInputChanged();
- mainUpdate(4);
- //updateMapMarkers();
- break;
- }
- case 'removenode': {
- var index = -1;
- for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
- if (index != -1) {
- var node = nodes[index];
- if (currentNode == node) {
- if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(2); }
- currentNode = null;
- // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
- }
- nodes.splice(index, 1);
- mainUpdate(4);
- //updateMapMarkers();
- }
- break;
- }
- case 'changenode': {
- var index = -1;
- for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
- if (index != -1) {
- var node = nodes[index];
- // Change the node
- node.name = message.event.node.name;
- node.rname = message.event.node.rname;
- node.lusers = message.event.node.lusers;
- node.users = message.event.node.users;
- node.host = message.event.node.host;
- node.desc = message.event.node.desc;
- node.publicip = message.event.node.publicip;
- node.iploc = message.event.node.iploc;
- node.wifiloc = message.event.node.wifiloc;
- node.gpsloc = message.event.node.gpsloc;
- node.tags = message.event.node.tags;
- node.ssh = message.event.node.ssh;
- node.rdp = message.event.node.rdp;
- node.userloc = message.event.node.userloc;
- node.rdpport = message.event.node.rdpport;
- node.rfbport = message.event.node.rfbport;
- node.sshport = message.event.node.sshport;
- node.httpport = message.event.node.httpport;
- node.httpsport = message.event.node.httpsport;
- node.consent = message.event.node.consent;
- node.pmt = message.event.node.pmt;
- if (message.event.node.agent != null) {
- if (node.agent == null) node.agent = {};
- if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
- if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
- if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
- if (message.event.node.agent.root != null) { node.agent.root = message.event.node.agent.root; }
- if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
- node.agent.tag = message.event.node.agent.tag;
- }
- if (message.event.node.intelamt != null) {
- if (node.intelamt == null) node.intelamt = {};
- if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
- if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
- if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; }
- if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
- if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
- if (message.event.node.intelamt.tag != null) { node.intelamt.tag = message.event.node.intelamt.tag; }
- if (message.event.node.intelamt.uuid != null) { node.intelamt.uuid = message.event.node.intelamt.uuid; }
- if (message.event.node.intelamt.realm != null) { node.intelamt.realm = message.event.node.intelamt.realm; }
- if (message.event.node.intelamt.flags != null) { node.intelamt.flags = message.event.node.intelamt.flags; }
- if (message.event.node.intelamt.warn != null) { node.intelamt.warn = message.event.node.intelamt.warn; } else { delete node.intelamt.warn; }
- }
- if (message.event.node.av != null) { node.av = message.event.node.av; }
- if (message.event.node.wsc != null) { node.wsc = message.event.node.wsc; }
- node.namel = node.name.toLowerCase();
- if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
- if (message.event.node.icon) { node.icon = message.event.node.icon; }
- if (message.event.node.lastbootuptime != null) { node.lastbootuptime = message.event.node.lastbootuptime; }
- //onSortSelectChange(true);
- //drawNotifications();
- refreshDevice(node._id);
- updateDeviceViewDevice(node);
- if (currentNode == node) { updateDeviceDetails(); }
- //if ((currentNode == node) && (xxdialogMode != null) && (xxdialogTag == '@xxmap')) { p10showNodeLocationDialog(); }
- }
- break;
- }
- case 'nodemeshchange': {
- var index = -1;
- for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
- if (index != -1) {
- var node = nodes[index];
- if ((meshes[message.event.newMeshId] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
- // We don't see the new mesh, remove this device
- // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
- if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); currentNode = null; }
- nodes.splice(index, 1);
- } else {
- // We see the new mesh, move this device
- node.meshid = message.event.newMeshId;
- node.meshnamel = meshes[message.event.newMeshId]?meshes[message.event.newMeshId].name.toLowerCase():'*';
- }
- mainUpdate(4);
- refreshDevice(message.event.nodeid);
- } else {
- // This is a new device, add it.
- var node = message.event.node;
- if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
- node.namel = node.name.toLowerCase();
- if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
- node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
- node.state = 0;
- if (!node.icon) node.icon = 1;
- node.ident = ++nodeShortIdent;
- if (nodes == null) { }
- nodes.push(node);
- // Web page update
- //mainUpdate(1 | 2 | 4 | 16);
- mainUpdate(4);
- }
- break;
- }
- case 'nodeconnect': {
- // Indicated a node has changed connectivity state
- var index = -1;
- for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
- if (index != -1) {
- var node = nodes[index];
- // Change the node connection state
- node.conn = message.event.conn;
- node.pwr = message.event.pwr;
- // Clear sesssion information if needed
- if ((node.conn & 1) == 0) { delete node.sessions; }
- refreshDevice(node._id);
- updateDeviceViewDevice(node);
- }
- break;
- }
- case 'login': {
- // Update the last login time
- if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) { users['user/' + domain + '/' + message.event.username.toLowerCase()].login = message.event.time; }
- break;
- }
- case 'notify': {
- var n = { text: message.event.value, title: message.event.title, icon: message.event.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
- if (message.id != null) { n.id = message.id; }
- if (message.event.tag != null) { n.tag = message.event.tag; }
- if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
- addNotification(n);
- break;
- }
- case 'sysinfohash': {
- // If the sysinfo document has changed and we are looking at it, request an update.
- if ((currentNode != null) && (message.event.nodeid == powerTimelineReq)) { meshserver.send({ action: 'getsysinfo', nodeid: message.event.nodeid }); }
- break;
- }
- case 'ifchange': {
- // Network interface changed for a device, if we are currently viewing this device, ask for an update.
- if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); }
- break;
- }
- case 'devicesessions': {
- // List of sessions for a given device
- var node = getNodeFromId(message.event.nodeid);
- if (node == null) break; // Unknown node
- node.sessions = message.event.sessions;
- if (node.sessions != null) {
- for (var i in node.sessions) { if (Object.keys(node.sessions[i]).length == 0) { delete node.sessions[i]; } }
- if (Object.keys(node.sessions).length == 0) { delete node.sessions; }
- }
- refreshDevice(message.event.nodeid);
- updateDeviceViewDevice(node);
- //if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
- // If we are looking at the sessions dialog box for this device now, update it
- if (xxdialogTag == ('SESSIONS-' + message.event.nodeid)) { showDeviceSessions(message.event.nodeid, true); }
- //if (xxdialogTag == ('MESSAGES-' + message.event.nodeid)) { showDeviceMessages(message.event.nodeid, true); }
- if (xxdialogTag == ('HELPREQ-' + message.event.nodeid)) { showDeviceHelpRequests(message.event.nodeid, true); }
- break;
- }
- case 'stopped': { // Server is stopping.
- // TODO: Disconnect
- break;
- }
- default:
- //console.log('Unknown message.event.action', message.event.action);
- break;
- }
- break;
- }
- case 'getcookie': {
- if (message.tag == 'novnc') {
- var vncurl = window.location.origin + domainUrl + 'novnc/vnc.html?ws=wss%3A%2F%2F' + window.location.host + encodeURIComponentEx(domainUrl) + (message.localRelay?'local':'mesh') + 'relay.ashx%3Fauth%3D' + message.cookie + '&show_dot=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&l={{{lang}}}';
- var node = getNodeFromId(message.nodeid);
- if (node != null) { vncurl += '&name=' + encodeURIComponentEx(node.name); }
- safeNewWindow(vncurl, 'mcnovnc/' + message.nodeid);
- } else if (message.tag == 'mstsc') {
- var rdpurl = window.location.origin + domainUrl + 'mstsc.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
- var node = getNodeFromId(message.nodeid);
- if (node != null) { rdpurl += '&name=' + encodeURIComponentEx(node.name); }
- if (message.localRelay) { rdpurl += '&local=1'; }
- safeNewWindow(rdpurl, 'mcmstsc/' + message.nodeid);
- } else if (message.tag == 'ssh') {
- var sshurl = window.location.origin + domainUrl + 'ssh.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
- var node = getNodeFromId(message.nodeid);
- if (node != null) { sshurl += '&name=' + encodeURIComponentEx(node.name); }
- if (message.localRelay) { sshurl += '&local=1'; }
- safeNewWindow(sshurl, 'mcssh/' + message.nodeid);
- }
- break;
- }
- default:
- //console.log('Unknown message.action', message.action);
- break;
- }
- }
- // To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
- var updateNaggleTimer = null;
- var updateNaggleFlags = 0;
- function mainUpdate(flags) {
- updateNaggleFlags |= flags;
- if (updateNaggleTimer == null) {
- updateNaggleTimer = setTimeout(function () {
- if (updateNaggleFlags & 1) { onSearchInputChanged(); }
- if (updateNaggleFlags & 4) { updateDevices(); updateDeviceDetails(); }
- if (updateNaggleFlags & 128) { updateMeshes(); }
- updateNaggleTimer = null;
- updateNaggleFlags = 0;
- gotoStartViewPage();
- }, 150);
- }
- }
- // Go to the correct starting view page
- function gotoStartViewPage() {
- var xviewmode = parseInt('{{viewmode}}');
- if (xxcurrentView > 1) return;
- if ('{{currentNode}}'.toLowerCase() != '') { // The .toLowerCase here is the minifier will not optimize this out.
- if (getNodeFromId('{{currentNode}}') == null) return; // This node is not loaded yet
- gotoDevice('{{currentNode}}', xviewmode);
- } else if (args.gotonode != null) {
- if (args.gotonode.length == 96) { args.gotonode = btoa(hex2rstr(args.gotonode)).split('+').join('@').split('/').join('$'); } // This is a HEX encoded NodeID, convert it to Base64
- if (getNodeFromId('node/' + domain + '/' + args.gotonode) == null) return; // This node is not loaded yet
- if (args.panel) { currentDevicePanel = parseInt(args.panel); }
- gotoDevice('node/' + domain + '/' + args.gotonode, xviewmode);
- } else if (args.gotodevicename != null) {
- var foundNode = null;
- if (nodes != null) { for (var i in nodes) { if (nodes[i].name == args.gotodevicename) { foundNode = nodes[i]._id; } } }
- if (foundNode) { gotoDevice(foundNode, xviewmode); go(xviewmode); }
- } else if (args.gotodevicername != null) {
- var foundNode = null;
- if (nodes != null) { for (var i in nodes) { if (nodes[i].rname == args.gotodevicername) { foundNode = nodes[i]._id; } } }
- if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
- } else if (args.gotodeviceip != null) {
- var foundNode = null;
- if (nodes != null) { for (var i in nodes) { if (nodes[i].ip == args.gotodeviceip) { foundNode = nodes[i]._id; } } }
- if (foundNode) { gotoDevice(foundNode, xviewmode); go(xviewmode); }
- } else if (args.gotomesh != null) {
- if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
- gotoMesh('mesh/' + domain + '/' + args.gotomesh);
- go(xviewmode);
- } else if (!isNaN(xviewmode)) {
- go(xviewmode);
- } else {
- setDialogMode(0);
- go(1);
- }
- delete args.gotonode;
- delete args.gotomesh;
- delete args.panel;
- if (xxcurrentView < 2) { go(2); }
- }
- //
- // Menu System
- //
- function topMenu(select) {
- if ((xxdialogMode != null) && (xxdialogMode != 0) && (xxdialogMode != 999)) return;
- if (select === undefined) {
- var x = (QS('topMenu').display == 'none');
- if (x == true) { if ((xxdialogMode == 0) || (xxdialogMode == null)) { QV('topMenu', true); xxdialogMode = 999; } } else { QV('topMenu', false); xxdialogMode = 0; }
- } else {
- QV('topMenu', false);
- xxdialogMode = 0;
- if ((select == 1) && (xxcurrentView != 3)) { goForward('account'); } // My Account
- if ((select == 2) && (xxcurrentView != 5)) { goForward('files'); } // My Files
- }
- }
- var backStack = [];
- function goBack() { if (xxdialogMode) return; if (backStack.length > 0) { backStack.pop(); } goStack(); }
- function goForward(id) { if (xxdialogMode) return; backStack.push(id); goStack(); }
- function goStack() {
- if (backStack.length == 0) { go(2); return; }
- var id = backStack[backStack.length - 1], idtype = id.split('/')[0];
- if (idtype == 'node') { setupDeviceMenu(0); gotoDevice(id); }
- if (idtype == 'mesh') { gotoMesh(id); }
- if (idtype == 'account') { go(3); }
- if (idtype == 'devices') { go(2); }
- if (idtype == 'files') {
- // Remind the user to add two factor authentication
- if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || (userinfo.otpduo > 0) || (userinfo.otpdev > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access this feature until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
- go(5);
- }
- }
- function updateFooterMenu(options) {
- while (options != null && options.length < 3) { options.push({ n: '' }); }
- var x = '', prev = '';
- if (options != null) { for (var i in options) { x += '<td style="cursor:pointer' + ((prev == '') ? '' : ';border-left:solid 1px white') + '" onclick="' + options[i].f + '">' + options[i].n; prev = options[i].n; } }
- QH('footerMenu', '<tr>' + x);
- }
- //
- // MY ACCOUNT
- //
- function account_viewPreviousLogins() {
- if (xxdialogMode) return;
- setDialogMode(2, "Previous Logins", 1, null, "Loading...", 'previousLogins');
- meshserver.send({ action: 'previousLogins' });
- }
- function account_manageImage(mode) {
- if (xxdialogMode) return;
- var user = (mode == 0) ? userinfo : currentUser;
- var x = '<input id=p2file type=file style=width:100% accept="image/*" onchange=account_manageImageEx()><div style=width:100%><canvas id=p2canvas width=256 height=256 style="width:256px;height:256px;margin-left:12px;margin-top:8px;border-radius:16px;box-shadow: 0px 0px 15px #000" onclick=account_canvasClick() /></div>';
- setDialogMode(2, "Manage Account Image", 7, account_manageImageEx2, x, user._id);
- var ctx = Q('p2canvas').getContext('2d');
- if (user.accountImageRnd == null) { user.accountImageRnd = Math.floor(Math.random() * 9999999999); }
- var arg = '';
- if (mode == 1) { arg = '&id=' + user._id.split('/')[2]; }
- var myImg = new Image();
- myImg.onload = function () { ctx.clearRect(0, 0, 256, 256); ctx.drawImage(myImg, 0, 0); };
- myImg.src = ((user.flags != null) && (user.flags & 1)) ? ('userimage.ashx?rnd=' + user.accountImageRnd + arg) : 'images/user-256.png';
- QE('idx_dlgDeleteButton', (user.flags != null) && (user.flags & 1));
- QE('idx_dlgOkButton', false);
- }
- function account_canvasClick() { Q('p2file').click(); }
- function account_manageImageEx() {
- var file = Q('p2file').files[0];
- var img = new Image;
- img.onload = function () {
- var cx = 0, cy = 0, min = Math.min(img.width, img.height);
- if (img.width > min) { cx = (img.width - min) / 2; }
- if (img.height > min) { cy = (img.height - min) / 2; }
- var ctx = Q('p2canvas').getContext('2d');
- ctx.imageSmoothingEnabled = true;
- ctx.webkitImageSmoothingEnabled = true;
- ctx.mozImageSmoothingEnabled = true;
- ctx.clearRect(0, 0, 256, 256);
- ctx.drawImage(img, cx, cy, min, min, 0, 0, 256, 256);
- QE('idx_dlgOkButton', true);
- }
- img.src = URL.createObjectURL(file);
- }
- function account_manageImageEx2(b, userid) {
- // Send updated image, or 0 if we pressed the delete button
- meshserver.send({ action: 'updateUserImage', userid: userid, image: (b == 2) ? 0 : Q('p2canvas').toDataURL('image/jpeg', 0.8) });
- //meshserver.send({ action: 'updateUserImage', image: (b == 2)?0:Q('p2canvas').toDataURL('image/png', 0.8) });
- }
- function toggleNightMode() {
- if (xxdialogMode) return;
- var cNightMode = getstore('nightMode', '0');
- var x = '<input type=radio id=night0 name=nightmoderadio value=0 ' + ((cNightMode == 0) ? 'checked' : '') + '><label for=night0>' + "Browser default" + '</label><br>';
- x += '<input type=radio id=night2 name=nightmoderadio value=2 ' + ((cNightMode == 2) ? 'checked' : '') + '><label for=night2>' + "Light mode" + '</label><br>';
- x += '<input type=radio id=night1 name=nightmoderadio value=1 ' + ((cNightMode == 1) ? 'checked' : '') + '><label for=night1>' + "Dark mode" + '</label><br>';
- setDialogMode(2, "Night Mode", 3, toggleNightModeEx, x);
- QV('uiMenu', false);
- }
- function toggleNightModeEx() {
- // Save new night mode
- var nNightMode = '0';
- if (Q('night1').checked) { nNightMode = '1'; }
- if (Q('night2').checked) { nNightMode = '2'; }
- putstore('nightMode', nNightMode);
- setNightMode();
- }
- function setNightMode() {
- // Set night mode
- var nNightMode = getstore('nightMode', '0')
- nightMode = false;
- if ((features2 & 0x00100000) != 0) { nNightMode = '1'; }
- if ((features2 & 0x00200000) != 0) { nNightMode = '2'; }
- if (nNightMode == '1') { nightMode = true; }
- else if ((nNightMode == '0') && (window.matchMedia)) { nightMode = window.matchMedia('(prefers-color-scheme: dark)').matches }
- if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; QS('body')['color'] = 'lightgray'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#FFF'; QS('body')['color'] = 'black'; }
- return nightMode;
- }
- function account_managePhone() {
- if (xxdialogMode || ((features & 0x02000000) == 0)) return;
- var x;
- if (userinfo.phone != null) {
- x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
- x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + userinfo.phone + '</div>';
- x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove phone number" + '</label></div>';
- setDialogMode(2, "Phone Notifications", 3, account_managePhoneRemove, x);
- account_managePhoneRemoveValidate();
- } else {
- x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>';
- x += '<td>Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.';
- x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=account_managePhoneValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneValidate(1)"></div></table>';
- setDialogMode(2, "Phone Notifications", 3, account_managePhoneAdd, x, 'verifyPhone');
- Q('d2phoneinput').focus();
- account_managePhoneValidate();
- }
- }
- function isPhoneNumber(x) { return x.match(/^\(?([0-9]{3,4})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) }
- function account_managePhoneValidate(x) { var ok = isPhoneNumber(Q('d2phoneinput').value); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } }
- function account_managePhoneCodeValidate(x) { var ok = (Q('d2phoneCodeInput').value.length == 6) && Q('d2phoneCodeInput').value.match(/[0-9]/); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } }
- function account_managePhoneConfirm(b, tag) { meshserver.send({ action: 'confirmPhone', code: Q('d2phoneCodeInput').value, cookie: tag }); }
- function account_managePhoneAdd() { if (isPhoneNumber(Q('d2phoneinput').value) == false) return; QE('d2phoneinput', false); meshserver.send({ action: 'verifyPhone', phone: Q('d2phoneinput').value }); }
- function account_managePhoneRemove() { if (Q('d2delPhone').checked) { meshserver.send({ action: 'removePhone' }); } }
- function account_managePhoneRemoveValidate() { QE('idx_dlgOkButton', Q('d2delPhone').checked); }
- function account_manageAuthEmail() {
- if (xxdialogMode || ((features & 0x00800000) == 0)) return;
- var emailU2Fenabled = ((userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
- setDialogMode(2, "Email Authentication", 1, function () {
- if (emailU2Fenabled != Q('email2facheck').checked) { meshserver.send({ action: 'otpemail', enabled: Q('email2facheck').checked }); }
- }, "When enabled, on each login, you will be given the option to receive a login token to you email account for added security." + '<br /><br /><label><input id=email2facheck type=checkbox ' + (emailU2Fenabled ? 'checked' : '') + '/>' + "Enable email two-factor authentication." + '</label>');
- }
- var loclist = { 'af': "Afrikaans", 'sq': "Albanian", 'ar': "Arabic (Standard)", 'ar-dz': "Arabic (Algeria)", 'ar-bh': "Arabic (Bahrain)", 'ar-eg': "Arabic (Egypt)", 'ar-iq': "Arabic (Iraq)", 'ar-jo': "Arabic (Jordan)", 'ar-kw': "Arabic (Kuwait)", 'ar-lb': "Arabic (Lebanon)", 'ar-ly': "Arabic (Libya)", 'ar-ma': "Arabic (Morocco)", 'ar-om': "Arabic (Oman)", 'ar-qa': "Arabic (Qatar)", 'ar-sa': "Arabic (Saudi Arabia)", 'ar-sy': "Arabic (Syria)", 'ar-tn': "Arabic (Tunisia)", 'ar-ae': "Arabic (U.A.E.)", 'ar-ye': "Arabic (Yemen)", 'an': "Aragonese", 'hy': "Armenian", 'as': "Assamese", 'ast': "Asturian", 'az': "Azerbaijani", 'eu': "Basque", 'bg': "Bulgarian", 'be': "Belarusian", 'bn': "Bengali", 'bs': "Bosnian", 'br': "Breton", 'my': "Burmese", 'ca': "Catalan", 'ch': "Chamorro", 'ce': "Chechen", 'zh': "Chinese", 'zh-hk': "Chinese (Hong Kong)", 'zh-cn': "Chinese (PRC)", 'zh-sg': "Chinese (Singapore)", 'zh-tw': "Chinese (Taiwan)", 'cv': "Chuvash", 'co': "Corsican", 'cr': "Cree", 'hr': "Croatian", 'cs': "Czech", 'da': "Danish", 'nl': "Dutch (Standard)", 'nl-be': "Dutch (Belgian)", 'en': "English", 'en-au': "English (Australia)", 'en-bz': "English (Belize)", 'en-ca': "English (Canada)", 'en-ie': "English (Ireland)", 'en-jm': "English (Jamaica)", 'en-nz': "English (New Zealand)", 'en-ph': "English (Philippines)", 'en-za': "English (South Africa)", 'en-tt': "English (Trinidad & Tobago)", 'en-gb': "English (United Kingdom)", 'en-us': "English (United States)", 'en-zw': "English (Zimbabwe)", 'eo': "Esperanto", 'et': "Estonian", 'fo': "Faeroese", 'fa': "Farsi (Persian)", 'fj': "Fijian", 'fi': "Finnish", 'fr': "French (Standard)", 'fr-be': "French (Belgium)", 'fr-ca': "French (Canada)", 'fr-fr': "French (France)", 'fr-lu': "French (Luxembourg)", 'fr-mc': "French (Monaco)", 'fr-ch': "French (Switzerland)", 'fy': "Frisian", 'fur': "Friulian", 'gd': "Gaelic (Scots)", 'gd-ie': "Gaelic (Irish)", 'gl': "Galacian", 'ka': "Georgian", 'de': "German (Standard)", 'de-at': "German (Austria)", 'de-de': "German (Germany)", 'de-li': "German (Liechtenstein)", 'de-lu': "German (Luxembourg)", 'de-ch': "German (Switzerland)", 'el': "Greek", 'gu': "Gujurati", 'ht': "Haitian", 'he': "Hebrew", 'hi': "Hindi", 'hu': "Hungarian", 'is': "Icelandic", 'id': "Indonesian", 'iu': "Inuktitut", 'ga': "Irish", 'it': "Italian (Standard)", 'it-ch': "Italian (Switzerland)", 'ja': "Japanese", 'kn': "Kannada", 'ks': "Kashmiri", 'kk': "Kazakh", 'km': "Khmer", 'ky': "Kirghiz", 'tlh': "Klingon", 'ko': "Korean", 'ko-kp': "Korean (North Korea)", 'ko-kr': "Korean (South Korea)", 'la': "Latin", 'lv': "Latvian", 'lt': "Lithuanian", 'lb': "Luxembourgish", 'mk': "FYRO Macedonian", 'ms': "Malay", 'ml': "Malayalam", 'mt': "Maltese", 'mi': "Maori", 'mr': "Marathi", 'mo': "Moldavian", 'nv': "Navajo", 'ng': "Ndonga", 'ne': "Nepali", 'no': "Norwegian", 'nb': "Norwegian (Bokmal)", 'nn': "Norwegian (Nynorsk)", 'oc': "Occitan", 'or': "Oriya", 'om': "Oromo", 'fa-ir': "Persian/Iran", 'pl': "Polish", 'pt': "Portuguese", 'pt-br': "Portuguese (Brazil)", 'pa': "Punjabi", 'pa-in': "Punjabi (India)", 'pa-pk': "Punjabi (Pakistan)", 'qu': "Quechua", 'rm': "Rhaeto-Romanic", 'ro': "Romanian", 'ro-mo': "Romanian (Moldavia)", 'ru': "Russian", 'ru-mo': "Russian (Moldavia)", 'sz': "Sami (Lappish)", 'sg': "Sango", 'sa': "Sanskrit", 'sc': "Sardinian", 'sd': "Sindhi", 'si': "Singhalese", 'sr': "Serbian", 'sk': "Slovak", 'sl': "Slovenian", 'so': "Somani", 'sb': "Sorbian", 'es': "Spanish", 'es-ar': "Spanish (Argentina)", 'es-bo': "Spanish (Bolivia)", 'es-cl': "Spanish (Chile)", 'es-co': "Spanish (Colombia)", 'es-cr': "Spanish (Costa Rica)", 'es-do': "Spanish (Dominican Republic)", 'es-ec': "Spanish (Ecuador)", 'es-sv': "Spanish (El Salvador)", 'es-gt': "Spanish (Guatemala)", 'es-hn': "Spanish (Honduras)", 'es-mx': "Spanish (Mexico)", 'es-ni': "Spanish (Nicaragua)", 'es-pa': "Spanish (Panama)", 'es-py': "Spanish (Paraguay)", 'es-pe': "Spanish (Peru)", 'es-pr': "Spanish (Puerto Rico)", 'es-es': "Spanish (Spain)", 'es-uy': "Spanish (Uruguay)", 'es-ve': "Spanish (Venezuela)", 'sx': "Sutu", 'sw': "Swahili", 'sv': "Swedish", 'sv-fi': "Swedish (Finland)", 'sv-sv': "Swedish (Sweden)", 'ta': "Tamil", 'tt': "Tatar", 'te': "Teluga", 'th': "Thai", 'tig': "Tigre", 'ts': "Tsonga", 'tn': "Tswana", 'tr': "Turkish", 'tk': "Turkmen", 'uk': "Ukrainian", 'hsb': "Upper Sorbian", 'ur': "Urdu", 've': "Venda", 'vi': "Vietnamese", 'vo': "Volapuk", 'wa': "Walloon", 'cy': "Welsh", 'xh': "Xhosa", 'ji': "Yiddish", 'zu': "Zulu" };
- var loclistex = { 'zh-chs': "Chinese (Simplified)", 'zh-cht': "Chinese (Traditional)" };
- function account_showLocalizationSettings() {
- if (xxdialogMode) return false;
- var n = getstore('loctag', 0), y = '';
- var x = '<select id=d2locselect style=width:180px><option value="*">' + "User browser value" + '</option>';
- for (var i in loclist) { x += '<option value="' + i + '"' + ((n == i)?' selected':'') + '>' + i + ' - ' + loclist[i] + '</option>'; }
- x += '</select>';
- if (serverinfo.languages && serverinfo.languages.length > 0) {
- y += "Changing the language will require a refresh of the page." + '<br /><br />';
- var z = '<select id=d2langselect style=width:180px><option value="*">' + "User browser value" + '</option>';
- for (var i in serverinfo.languages) {
- var lang = serverinfo.languages[i];
- z += '<option value="' + lang + '"' + ((userinfo.lang == lang)?' selected':'') + '>' + lang + ' - ' + (loclist[lang]?loclist[lang]:loclistex[lang]) + '</option>';
- }
- z += '</select>';
- y += addHtmlValue("Language", z);
- }
- y += addHtmlValue("Dates & Time", x);
- if ((userinfo.siteadmin == 0xFFFFFFFF) && (domain == '')) {
- y += '<br /><a rel="noreferrer noopener" target="_blank" href="translator.htm">' + "Help translate MeshCentral" + '</a>';
- }
- setDialogMode(2, "Localization Settings", 3, account_showLocalizationSettingsEx, y);
- return false;
- }
- function account_showLocalizationSettingsEx() {
- // Set user language
- var lang = Q('d2langselect').value;
- if ((lang == '*') && (userinfo.lang == null)) { lang = userinfo.lang; }
- if (lang != userinfo.lang) { meshserver.send({ action: 'changelang', lang: lang }); }
- // Set date localization
- var n = getstore('loctag', 0);
- var m = Q('d2locselect').value;
- if (n != m) {
- if (m != '*') { args.locale = m; } else { delete args.locale; }
- putstore('loctag', args.locale);
- mainUpdate(0xFFFFFFFF); // Refresh everything.
- }
- }
- function account_manageAuthApp() {
- if (xxdialogMode || ((features & 4096) == 0)) return;
- if (userinfo.otpsecret == 1) { account_removeOtp(); } else { account_addOtp(); }
- }
- function account_addOtp() {
- if (xxdialogMode || (userinfo.otpsecret == 1) || ((features & 4096) == 0)) return;
- setDialogMode(2, "Authenticator App", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, '<div id=d2optinfo>' + "Loading..." + '</div>', 'otpauth-request');
- meshserver.send({ action: 'otpauth-request' });
- }
- function account_addOtpCheck(e) {
- var tokenIsValid = (Q('d2otpauthinput').value.length == 6);
- QE('idx_dlgOkButton', tokenIsValid);
- if (e && (e.keyCode == 13) && tokenIsValid) { dialogclose(1); }
- }
- function account_removeOtp() {
- if (xxdialogMode || (userinfo.otpsecret != 1) || ((features & 4096) == 0)) return;
- setDialogMode(2, "Authenticator App", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of authenticator application 2-step login?");
- }
- function account_manageOtp(action) {
- if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); }
- if (xxdialogMode || ((features & 4096) == 0) || ((userinfo.otpsecret != 1) && (userinfo.otphkeys < 1))) return;
- meshserver.send({ action: 'otpauth-getpasswords', subaction: action });
- }
- function account_showVerifyEmail() {
- if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return;
- var x = "Click ok to send a verification mail to:" + '<br /><div style=padding:8px><b>' + EscapeHtml(userinfo.email) + '</b></div>' + "Please wait a few minute to receive the verification.";
- setDialogMode(2, "Email Verification", 3, account_showVerifyEmailEx, x);
- }
- function account_showVerifyEmailEx() {
- meshserver.send({ action: 'verifyemail', email: userinfo.email });
- }
- function account_showChangeEmail() {
- if (xxdialogMode) return;
- var x = addHtmlValue("Email", '<input id=dp3email style=width:170px maxlength=256 onchange=account_validateEmail() onkeyup=account_validateEmail(event) />');
- setDialogMode(2, "Email Address Change", 3, account_changeEmail, x);
- if (userinfo.email != null) { Q('dp3email').value = userinfo.email; }
- account_validateEmail();
- Q('dp3email').focus();
- }
- function account_validateEmail(e, email) {
- QE('idx_dlgOkButton', validateEmail(Q('dp3email').value) && (Q('dp3email').value != userinfo.email));
- if ((e != null) && (e.keyCode == 13)) { dialogclose(1); }
- }
- function account_changeEmail() {
- meshserver.send({ action: 'changeemail', email: Q('dp3email').value });
- }
- function account_showDeleteAccount() {
- if (xxdialogMode) return;
- var x = '<form method=post><table style=margin-left:10px><input type=hidden name=action value=deleteaccount /><input type=hidden name=authcookie value=' + authCookie + ' /><tr>';
- x += '<td align=right>' + "Password:" + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>';
- x += '</tr><tr><td align=right>' + "Password:" + '</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>';
- x += '</tr></table><div style=padding:10px;margin-bottom:4px>';
- x += '<input id=account_dlgCancelButton type=button value="' + "Cancel" + '" style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>';
- x += '<input id=account_dlgOkButton type=submit value="' + "OK" + '" style="float:right;width:80px" onclick=dialogclose(1)>';
- x += '</div><br /></form>';
- setDialogMode(2, "Delete Account", 0, null, x);
- account_validateDeleteAccount();
- Q('apassword1').focus();
- }
- function account_showChangePassword() {
- if (xxdialogMode) return false;
- var x = '<table style=margin-left:10px>';
- x += '<tr><td align=right>' + nobreak("Old password:") + '</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>';
- x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>';
- x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>';
- if (features & 0x00010000) { x += '<tr><td align=right>' + "Password hint:" + '</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>'; }
- x += '</table>'
- if (passRequirements) {
- var r = [], rc = 0;
- for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } }
- if (rc > 0) { x += '<br /><span style=font-size:x-small>' + format("Requirements: {0}.", r.join(', ')) + '</span>'; }
- }
- x += '<br />';
- setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x);
- Q('apassword0').focus();
- account_validateNewPassword();
- return false;
- }
- function account_showChangePasswordEx() {
- if (Q('apassword1').value == Q('apassword2').value) {
- var r = { action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value };
- if (features & 0x00010000) { r.hint = Q('apasswordhint').value; }
- meshserver.send(r);
- }
- }
- function account_createMesh() {
- if (xxdialogMode) return;
- // Check if we are disallowed from creating a device group
- if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return; }
- // Remind the user to verify the email address
- if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; }
- // Remind the user to add two factor authentication
- if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || (userinfo.otpduo > 0) || (userinfo.otpdev > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
- // We are allowed, let's prompt to information
- var x = addHtmlValue("Name", '<input id=dp3meshname style=width:170px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
- x += addHtmlValue("Type", '<div style=width:170px;margin:0;padding:0><select id=dp3meshtype style=width:100% onchange=account_validateMeshCreate() ><option value=2>' + "Software Agent Group" + '</option><option value=1>' + "Intel® AMT only" + '</option></select></div>');
- x += addHtmlValue("Description", '<div style=width:170px;margin:0;padding:0><textarea id=dp3meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
- setDialogMode(2, "Create Device Group", 3, account_createMeshEx, x);
- account_validateMeshCreate();
- Q('dp3meshname').focus();
- }
- function account_validateMeshCreate() {
- QE('idx_dlgOkButton', Q('dp3meshname').value.length > 0);
- }
- function account_createMeshEx(button, tag) {
- meshserver.send({ action: 'createmesh', meshname: Q('dp3meshname').value, meshtype: parseInt(Q('dp3meshtype').value), desc: Q('dp3meshdesc').value });
- }
- function account_validateDeleteAccount() {
- QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value));
- }
- function account_validateNewPassword() {
- var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value);
- if ((features & 0x00010000) && (Q('apasswordhint').value == Q('apassword1').value)) { ok = false; }
- if (Q('apassword1').value != '') {
- if (passRequirements == null || passRequirements == '') {
- // No password requirements, display password strength
- var passStrength = checkPasswordStrength(Q('apassword1').value);
- if (passStrength >= 80) { r = '<span style=color:green>Strong<span>'; } else if (passStrength >= 60) { r = '<span style=color:blue>●<span>'; } else { r = '<span style=color:red>●<span>'; }
- } else {
- // Password requirements provided, use that
- var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements);
- if (passReq == false) { ok = false; r = '<span style=color:red>' + "Policy" + '<span>' }
- }
- }
- QH('dxPassWarn', r);
- //QE('account_dlgOkButton', ok);
- QE('idx_dlgOkButton', ok);
- }
- // Return a password strength score
- function checkPasswordStrength(password) {
- var r = 0, letters = {}, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) }
- if (!password) return 0;
- for (var i = 0; i < password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; }
- for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; }
- return parseInt(r + (varCount - 1) * 10);
- }
- // Check password requirements
- function checkPasswordRequirements(password, requirements) {
- if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true;
- if (requirements.min) { if (password.length < requirements.min) return false; }
- if (requirements.max) { if (password.length > requirements.max) return false; }
- var numeric = 0, lower = 0, upper = 0, nonalpha = 0;
- for (var i = 0; i < password.length; i++) {
- if (/\d/.test(password[i])) { numeric++; }
- if (/[a-z]/.test(password[i])) { lower++; }
- if (/[A-Z]/.test(password[i])) { upper++; }
- if (/\W/.test(password[i])) { nonalpha++; }
- }
- if (requirements.numeric && (numeric < requirements.numeric)) return false;
- if (requirements.lower && (lower < requirements.lower)) return false;
- if (requirements.upper && (upper < requirements.upper)) return false;
- if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false;
- return true;
- }
- function updateMeshes() {
- var r = '', count = 0;
- for (i in meshes) {
- count++;
- // Mesh rights
- var meshrights = GetMeshRights(meshes[i]);
- var rights = "Partial Rights";
- if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights";
- // Print the mesh information
- r += '<div style=cursor:pointer onclick=goForward(\'' + i + '\')>';
- r += '<div style="float:left;margin-left:4px"><img src="/images/meshicon50.png" width=50 height=50 /></div>';
- r += '<div class=meshList>';
- r += '<div><div style=color:black;padding-left:12px;padding-top:2px><b>' + EscapeHtml(meshes[i].name) + '</b></div><div style=padding-left:12px;padding-top:3px;color:black>' + rights + '</div></div>';
- r += '</div></div>';
- }
- QH('p3meshes', r);
- QV('p3noMeshFound', count == 0);
- }
- function gotoMesh(meshid) {
- // Remind the user to add two factor authentication
- if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || (userinfo.otpduo > 0) || (userinfo.otpdev > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access this feature until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
- currentMesh = meshes[meshid];
- if (currentMesh == null) { goBack(); }
- p20updateMesh();
- go(20);
- }
- //
- // FILE SELECTOR, DIALOG 3
- //
- function d3init() {
- d3fileoptions = { dialog: 1, filter: 'd3filter', files: 'd3serverfiles', folderup: 'p3FolderUp', currentFolder: 'p3CurrentFolder', func: d3setActions };
- Q('d3localFile').value = '';
- Q('d3localFile').accept = Q('d3filter').value;
- d3modechange();
- }
- function d3modechange() {
- var mode = Q('d3uploadMode').value;
- QV('d3localmode', mode == 1);
- QV('d3servermode', mode == 2);
- if (mode == 1) { d3setActions(); } else { d3updatefiles(); }
- }
- var d3filetreelinkpath;
- var d3filetreelocation = [];
- var d3fileoptions = null;
- function d3updatefiles() {
- if (d3fileoptions == null) return;
- if ((d3fileoptions.filter == 'd3filter') && (Q('d3uploadMode').value == 1)) return;
- var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1, publicPath = null, lastFolderName = '';
- // Navigate to path location, build the paths at the same time
- var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
- for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes
- d3filetreelinkpath = '';
- for (var i in d3filetreelocation) {
- if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
- d3filetreelocation2.push(d3filetreelocation[i]);
- if ((folderdepth == 1)) {
- var sp = d3filetreelocation[i].split('/');
- publicPath = window.location.origin + domainUrl + sp[0] + 'files/' + sp[2];
- if (d3filetreelocation[i] === userinfo._id) { d3filetreelinkpath += 'self'; } else { d3filetreelinkpath += (sp[0] + '/' + sp[2]); }
- } else {
- if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
- }
- filetreex = filetreex.f[d3filetreelocation[i]];
- lastFolderName = filetreex.n;
- folderdepth++;
- } else {
- break;
- }
- }
- d3filetreelocation = d3filetreelocation2; // In case we could not go down the full path, we set the new path location here.
- // Sort the files
- var filetreexx = p5sort_files(filetreex.f);
- // File filter
- var fileFilter = '';
- if (d3fileoptions.filter) { fileFilter = Q(d3fileoptions.filter).value };
- // Display all files and folders at this location
- for (var i in filetreexx) {
- // Figure out the name and shortname
- var f = filetreexx[i], name = f.n, shortname;
- // Filter out files
- if ((f.t == 3) && (fileFilter != '') && (f.nx.toLowerCase().endsWith(fileFilter) == false)) { continue; }
- // if (name.length > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + ("..." + '</span>'); } else { shortname = EscapeHtml(name); }
- // Removed redundant filename length check because we handle it in the CSS
- shortname = EscapeHtml(name);
- // Figure out the size
- var fsize = '';
- if (f.s != null) { fsize = getFileSizeStr(f.s); }
- var h = '';
- if (f.t != 3) {
- var title = '';
- h = '<div class=filelist file=999><span style=float:right title="' + title + '"></span><span title="' + shortname + '"><div class=fileIcon' + f.t + ' onclick=d3folderset("' + encodeURIComponentEx(f.nx) + '")></div> <a href=# style=cursor:pointer onclick=\'return d3folderset("' + encodeURIComponentEx(f.nx) + '")\'>' + shortname + '</a></span></div>';
- } else {
- var link = shortname;
- //if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=" + encodeURIComponentEx(filetreelinkpath + '/' + f.nx) + "\">" + shortname + "</a>"; }
- h = '<div class=filelist file=3><input style=float:left name=fcx class=fcb type=checkbox onchange=d3setActions() value="' + f.nx + '"> <span style=float:right>' + EscapeHtml(fsize) + '</span><span title="' + shortname + '"><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
- }
- if (f.t < 3) { html1 += h; } else { html2 += h; }
- }
- if (d3fileoptions.currentFolder) { QH(d3fileoptions.currentFolder, lastFolderName); }
- QH(d3fileoptions.files, html1 + html2);
- QE(d3fileoptions.folderup, d3filetreelocation.length > 0);
- if (d3fileoptions.func) { d3fileoptions.func(); }
- }
- function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); return false; }
- function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
- function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; }
- function d3setActions() {
- if (d3fileoptions.dialog == 1) {
- var mode = Q('d3uploadMode').value;
- if (mode == 1) {
- QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
- } else {
- QE('idx_dlgOkButton', d3getFileSel().length == 1);
- }
- } else if (d3fileoptions.dialog == 2) {
- QE('idx_dlgOkButton', d3getFileSel().length == 1);
- }
- }
- //
- // MY FILES
- //
- var filetreelinkpath;
- var filetreelocation = [];
- function p5refreshFiles() { meshserver.send({ action: 'files' }); }
- function updateFiles() {
- QV('MainMenuMyFiles', ((features & 8) == 0));
- if ((features & 8) != 0) return; // If running on a server without files, exit now.
- var html1 = '', html2 = '', displayPath = '<a style=cursor:pointer;color:black onclick=p5folderup(0)>' + "Root" + '</a>', fullPath = 'Root', publicPath, filetreex = filetree, folderdepth = 1;
- // Navigate to path location, build the paths at the same time
- var filetreelocation2 = [], oldlinkpath = filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc');
- for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes
- filetreelinkpath = '';
- for (var i in filetreelocation) {
- if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) {
- filetreelocation2.push(filetreelocation[i]);
- fullPath += ' / ' + filetreelocation[i];
- if ((folderdepth == 1)) {
- var sp = filetreelocation[i].split('/');
- publicPath = window.location.origin + domainUrl + sp[0] + 'files/' + sp[2];
- //if (filetreelocation[i] === userinfo._id) { filetreelinkpath += 'self'; } else { filetreelinkpath += (sp[0] + '/' + sp[2]); }
- filetreelinkpath += filetreelocation[i];
- } else {
- if (filetreelinkpath != '') { filetreelinkpath += '/' + filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + filetreelocation[i]; } }
- }
- filetreex = filetreex.f[filetreelocation[i]];
- displayPath += ' / <a style=cursor:pointer;color:black onclick=p5folderup(' + folderdepth + ')>' + EscapeHtml(filetreex.n != null ? filetreex.n : filetreelocation[i]) + '</a>';
- folderdepth++;
- } else {
- break;
- }
- }
- filetreelocation = filetreelocation2; // In case we could not go down the full path, we set the new path location here.
- var publicfolder = fullPath.toLowerCase().startsWith('root / ' + userinfo._id + ' / public');
- // Sort the files
- var filetreexx = p5sort_files(filetreex.f);
- // Display all files and folders at this location
- for (var i in filetreexx) {
- // Figure out the name and shortname
- var f = filetreexx[i], name = f.n, shortname;
- // if (name.length > 40) { shortname = EscapeHtml(name.substring(0, 40)) + "..."; } else { shortname = EscapeHtml(name); }
- // Removed redundant filename length check because we handle it in the CSS
- shortname = EscapeHtml(name);
- // Figure out the date
- //var fdatestr = '';
- //if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + '/' + (fdate.getDate()) + '/' + fdate.getFullYear() + ' ' + printTime(fdate) + ' '; }
- // Figure out the size
- var fsize = '';
- if (f.s != null) { fsize = getFileSizeStr(f.s); }
- var h = '';
- if (f.t < 3 || f.t == 4) {
- var right = (f.t == 1 || f.t == 4) ? p5getQuotabar(f) : '';
- h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + EscapeHtml(name) + '\'> <span style=float:right;padding-right:4px>' + right + '</span><span title="' + shortname + '"><div class=fileIcon' + f.t + '></div><a style=cursor:pointer onclick=p5folderset("' + encodeURIComponent(f.nx) + '")>' + shortname + '</a></span></div>';
- } else {
- var link = shortname;
- var publiclink = '';
- if (publicfolder) { publiclink = ' (<a style=cursor:pointer onclick=\'p5showPublicLink("' + publicPath + '/' + f.nx + '")\'>' + "Link" + '</a>)'; }
- if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '\">' + shortname + '</a>' + publiclink; }
- h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + f.nx + '\'> <span style=float:right;padding-right:4px>' + EscapeHtml(fsize) + '</span><span title="' + shortname + '"><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
- }
- if (f.t < 3) { html1 += h; } else { html2 += h; }
- }
- //if (f.parent == null) { }
- QH('p5rightOfButtons', p5getQuotabar(filetreex));
- QH('p5files', html1 + html2);
- QH('p5currentpath', displayPath);
- QE('p5FolderUp', filetreelocation.length != 0);
- QV('p5PublicShare', publicfolder);
- // Re-check all boxes if needed
- if (oldlinkpath == filetreelinkpath) {
- checkboxes = document.getElementsByName('fc');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].checked = (checkedBoxes.indexOf(checkboxes[i].value) >= 0);
- }
- }
- p5setActions();
- }
- function getNiceSize(bytes) {
- if (bytes <= 0) return "Storage exceed";
- if (bytes < 2048) return format("{0}b left", bytes);
- if (bytes < 2097152) return format("{0}k left", Math.round(bytes / 1024));
- if (bytes < 2147483648) return format("{0}m left", Math.round(bytes / 1024 / 1024));
- return format("{0}g left", Math.round(bytes / 1024 / 1024 / 1024));
- }
- function getNetworkSpeed(bitsPerSecond) {
- if (bitsPerSecond <= 0) return "0 bps";
- if (bitsPerSecond < 1000) return format("{0} bps", bitsPerSecond);
- if (bitsPerSecond < 1000000) return format("{0} Kbps", Math.round(bitsPerSecond / 1000));
- if (bitsPerSecond < 1000000000) return format("{0} Mbps", Math.round(bitsPerSecond / 1000000));
- return format("{0} Gbps", (bitsPerSecond / 1000000000).toFixed(1));
- }
- function p5getQuotabar(f) {
- while (f.t > 1 && f.t != 4) { f = f.parent; }
- if ((f.t != 1 && f.t != 4) || (f.maxbytes == null)) return '';
- return getNiceSize(f.maxbytes - f.s) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' />';
- }
- function p5showPublicLink(u) { setDialogMode(2, "Public Link", 1, null, '<input type=text style=width:100% value="' + u + '" readonly />'); }
- var sortorder;
- function p5sort_filename(a, b) { if (a.ln > b.ln) return (1 * sortorder); if (a.ln < b.ln) return (-1 * sortorder); return 0; }
- function p5sort_timestamp(a, b) { if (a.d > b.d) return (1 * sortorder); if (a.d < b.d) return (-1 * sortorder); return 0; }
- function p5sort_bysize(a, b) { if (a.s == b.s) return p5sort_filename(a, b); return (((a.s - b.s)) * sortorder); }
- function p5sort_files(files) {
- var r = [], sortselection = Q('p5sortdropdown').value;
- for (var i in files) { files[i].nx = i; if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
- sortorder = 1;
- if (sortselection > 3) { sortorder = -1; sortselection -= 3; }
- if (sortselection == 1) { r.sort(p5sort_filename); }
- else if (sortselection == 2) { r.sort(p5sort_bysize); }
- else if (sortselection == 3) { r.sort(p5sort_timestamp); }
- return r;
- }
- function p5setActions() {
- var cc = getFileSelCount(), tc = getFileCount(), sfc = getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders)
- QE('p5DeleteFileButton', (cc > 0) && (filetreelocation.length > 0));
- QE('p5NewFolderButton', filetreelocation.length > 0);
- QE('p5UploadButton', filetreelocation.length > 0);
- QE('p5RenameFileButton', (cc == 1) && (filetreelocation.length > 0));
- QE('p5SelectAllButton', tc > 0);
- Q('p5SelectAllButton').value = (cc > 0 ? "None" : "All");
- QE('p5CutButton', (sfc > 0) && (cc == sfc));
- QE('p5CopyButton', (sfc > 0) && (cc == sfc));
- QE('p5PasteButton', (p5clipboard != null) && (p5clipboard.length > 0) && (filetreelocation.length > 0));
- }
- function getFileSelCount(includeDirs) { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; }
- function getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; }
- function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; }
- function p5selectallfile() { var nv = (getFileSelCount() == 0), checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p5setActions(); }
- function setupBackPointers(x) { if (x.f != null) { var fs = 0, fc = 0; for (var i in x.f) { setupBackPointers(x.f[i]); x.f[i].parent = x; if (x.f[i].s) { fs += x.f[i].s; } if (x.f[i].c) { fc += x.f[i].c; } if (x.f[i].t == 3) { fc++; } } x.s = fs; x.c = fc; } return x; }
- function getFileSizeStr(size) { if (size == 1) return "1 byte"; return format("{0} bytes", size); }
- function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); return false; }
- function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); return false; }
- function p5createfolder() { setDialogMode(2, "New Folder", 3, p5createfolderEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% />'); focusTextBox('p5renameinput'); p5fileNameCheck(); }
- function p5createfolderEx() { meshserver.send({ action: 'fileoperation', fileop: 'createfolder', path: filetreelocation, newfolder: Q('p5renameinput').value }); }
- function p5deletefile() { var cc = getFileSelCount(), rec = (getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p5recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p5recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p5deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); }
- function p5deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(checkboxes[i].value); } } meshserver.send({ action: 'fileoperation', fileop: 'delete', path: filetreelocation, delfiles: delfiles, rec: Q('p5recdeleteinput').checked }); }
- function p5renamefile() { var renamefile, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = checkboxes[i].value; } } setDialogMode(2, "Rename", 3, p5renamefileEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'fileoperation', fileop: 'rename', path: filetreelocation, oldname: renamefile }); focusTextBox('p5renameinput'); p5fileNameCheck(); }
- function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); }
- function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } }
- var isFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
- function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value="' + encodeURIComponent(filetreelinkpath) + '" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /></form>'); updateUploadDialogOk('p5uploadinput'); }
- function p5uploadFileEx() { Q('p5loginSubmit').click(); }
- function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); }
- var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0;
- function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); }
- function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confirm {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0 ? 'copy' : 'move'), p5clipboard.length, ((p5clipboard.length > 1) ? 's' : '')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); }
- function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0 ? 'copy' : 'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } }
- function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1) ? 's' : ''), (p5clipboardCut == 0 ? "copy" : "move")) + ', <a href=# onclick="return p5clearClip()" style=cursor:pointer>' + "Clear" + '</a>.' } QH('p5bottomstatus', x); p5setActions(); }
- function p5clearClip() { p5clipboard = null; p5clipboardFolder = null; p5clipboardCut = 0; p5updateClipview(); return false; }
- function p5fileDragDrop(e) {
- haltEvent(e);
- QV('bigfail', false);
- QV('bigok', false);
- //QV('p5fileCatchAllInput', false);
- if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || filetreelocation.length == 0) return;
- var names = [], sizes = [], types = [], datas = [], readercount = e.dataTransfer.files.length;
- for (var i = 0; i < e.dataTransfer.files.length; i++) {
- var reader = new FileReader(), file = e.dataTransfer.files[i];
- names.push(file.name);
- sizes.push(file.size);
- types.push(file.type);
- reader.onload = function (event) {
- datas.push(event.target.result);
- if (--readercount == 0) {
- Q('p5fileDragName').value = names.join('*');
- Q('p5fileDragSize').value = sizes.join('*');
- Q('p5fileDragType').value = types.join('*');
- Q('p5fileDragData').value = datas.join('*');
- Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
- Q('p5loginSubmit2').click();
- }
- }
- reader.readAsDataURL(file);
- }
- }
- var p5dragtimer = null;
- function p5fileDragOver(e) {
- haltEvent(e);
- if (p5dragtimer != null) { clearTimeout(p5dragtimer); p5dragtimer = null; }
- var ac = true; // TODO: Set to true if we can accept the file
- if (filetreelocation.length == 0) { ac = false; }
- QV('bigok', ac);
- QV('bigfail', !ac);
- //QV('p5fileCatchAllInput', ac);
- }
- function p5fileDragLeave(e) {
- haltEvent(e);
- if (e.target.id != 'p5filetable') {
- QV('bigfail', false);
- QV('bigok', false);
- //QV('p5fileCatchAllInput', false);
- } else {
- p5dragtimer = setTimeout('QV(\'bigfail\',false);QV(\'bigok\',false);p5dragtimer=null;', 200);
- }
- }
- //
- // MY DEVICES
- //
- function onRealNameCheckBox() {
- showRealNames = Q('RealNameCheckBox').checked;
- putstore('showRealNames', showRealNames ? 1 : 0);
- mainUpdate(5);
- }
- function onOnlineCheckBox(e) {
- putstore('onlineOnly', Q('OnlineCheckBox').checked ? 1 : 0);
- onSearchInputChanged();
- }
- function updateDevicePageState() {
- if ((devicePagingState == null) || (devicePagingState.total <= devicePagingState.limit)) {
- QV('devViewPageState', false);
- QV('devViewPageButton2', false);
- QV('devViewPageButton3', false);
- } else {
- var currentPage = Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit);
- var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
- QV('devViewPageState', true);
- QV('devViewPageButton2', true);
- QV('devViewPageButton3', true);
- QH('devViewPageState', currentPage + '/' + maxPage);
- }
- }
- function onDeviceViewPageChange(i) {
- if (devicePagingState == null) return;
- var currentPage = (Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit));
- var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
- switch (i) {
- case 2: { if (currentPage > 1) meshserver.send({ action: 'nodes', skip: (currentPage - 2) * devicePagingState.limit }); break; } // Goto previous page
- case 3: { if (currentPage < maxPage) meshserver.send({ action: 'nodes', skip: currentPage * devicePagingState.limit }); break; } // Goto next page
- }
- }
- function onDeviceSearchChanged(e) {
- setTimeout(function () { onSearchInputChanged(); }, 10);
- }
- function clearSearchInput() {
- Q('SearchInput').value = '';
- Q('OnlineCheckBox').checked = false;
- onSearchInputChanged();
- }
- function onSearchInputChanged() {
- var x = Q('SearchInput').value.toLowerCase().trim(); putstore('_search', Q('SearchInput').value);
- QS('SearchInput')['background-color'] = (x == '') ? '#FFFFFF' : '#FDFFBE';
- var userSearch = null, ipSearch = null, groupSearch = null, tagSearch = null, agentTagSearch = null, wscSearch = null, osSearch = null, amtSearch = null;
- if (x.startsWith('user:'.toLowerCase())) { userSearch = x.substring('user:'.length); }
- else if (x.startsWith('u:'.toLowerCase())) { userSearch = x.substring('u:'.length); }
- else if (x.startsWith('ip:'.toLowerCase())) { ipSearch = x.substring('ip:'.length); }
- else if (x.startsWith('group:'.toLowerCase())) { groupSearch = x.substring('group:'.length); }
- else if (x.startsWith('g:'.toLowerCase())) { groupSearch = x.substring('g:'.length); }
- else if (x.startsWith('tag:'.toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring('tag:'.length); }
- else if (x.startsWith('t:'.toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring('t:'.length); }
- else if (x.startsWith('atag:'.toLowerCase())) { agentTagSearch = Q('SearchInput').value.trim().substring('atag:'.length).toLowerCase(); }
- else if (x.startsWith('a:'.toLowerCase())) { agentTagSearch = Q('SearchInput').value.trim().substring('a:'.length).toLowerCase(); }
- else if (x.startsWith('os:'.toLowerCase())) { osSearch = Q('SearchInput').value.trim().substring('os:'.length).toLowerCase(); }
- else if (x.startsWith('amt:'.toLowerCase())) { amtSearch = Q('SearchInput').value.trim().substring('amt:'.length).toLowerCase(); }
- else if (x == 'wsc:ok') { wscSearch = 1; }
- else if (x == 'wsc:noav') { wscSearch = 2; }
- else if (x == 'wsc:noupdate') { wscSearch = 3; }
- else if (x == 'wsc:nofirewall') { wscSearch = 4; }
- else if (x == 'wsc:any') { wscSearch = 5; }
- if (x == '') {
- // No search
- for (var d in nodes) { nodes[d].v = true; }
- } else if (ipSearch != null) {
- // IP address search
- for (var d in nodes) { nodes[d].v = ((nodes[d].ip != null) && (nodes[d].ip.indexOf(ipSearch) >= 0)); }
- } else if (groupSearch != null) {
- // Group filter
- for (var d in nodes) { nodes[d].v = (meshes[nodes[d].meshid].name.toLowerCase().indexOf(groupSearch) >= 0); }
- } else if (tagSearch != null) {
- // Tag filter
- for (var d in nodes) {
- nodes[d].v = ((nodes[d].tags == null) && (tagSearch == '')) || ((nodes[d].tags != null) && (nodes[d].tags.indexOf(tagSearch) >= 0));
- }
- } else if (agentTagSearch != null) {
- // Agent Tag filter
- for (var d in nodes) {
- nodes[d].v = (((nodes[d].agent != null) && (nodes[d].agent.tag == null)) && (agentTagSearch == '')) || ((nodes[d].agent != null) && (nodes[d].agent.tag != null) && (nodes[d].agent.tag.toLowerCase().indexOf(agentTagSearch) >= 0));
- }
- } else if (userSearch != null) {
- // User search
- for (var d in nodes) {
- nodes[d].v = false;
- if (nodes[d].users && nodes[d].users.length > 0) { for (var i in nodes[d].users) { if (nodes[d].users[i].toLowerCase().indexOf(userSearch) >= 0) { nodes[d].v = true; } } }
- }
- } else if (osSearch != null) {
- // OS search
- for (var d in nodes) { nodes[d].v = ((nodes[d].osdesc != null) && (nodes[d].osdesc.toLowerCase().indexOf(osSearch) >= 0)); }
- } else if (amtSearch != null) {
- // Intel AMT search
- for (var d in nodes) { nodes[d].v = (nodes[d].intelamt != null) && ((amtSearch == '') || (nodes[d].intelamt.state == amtSearch)); }
- } else if (wscSearch != null) {
- // Windows Security Center
- for (var d in nodes) {
- nodes[d].v = false;
- if (nodes[d].wsc) {
- if ((wscSearch == 1) && (nodes[d].wsc.antiVirus == 'OK') && (nodes[d].wsc.autoUpdate == 'OK') && (nodes[d].wsc.firewall == 'OK')) { nodes[d].v = true; }
- else if (((wscSearch == 2) || (wscSearch == 5)) && (nodes[d].wsc.antiVirus != 'OK')) { nodes[d].v = true; }
- else if (((wscSearch == 3) || (wscSearch == 5)) && (nodes[d].wsc.autoUpdate != 'OK')) { nodes[d].v = true; }
- else if (((wscSearch == 4) || (wscSearch == 5)) && (nodes[d].wsc.firewall != 'OK')) { nodes[d].v = true; }
- }
- }
- } else if (x == '*') {
- // Star filter
- for (var d in nodes) { nodes[d].v = (stars[nodes[d]._id] == 1); }
- } else {
- // Device name search
- try {
- var rs = x.split(/\s+/).join('|'), rx = new RegExp(rs); // In some cases (like +), this can throw an exception.
- for (var d in nodes) {
- if (features2 & 0x00008000) {
- if(features2 & 0x10000000){
- nodes[d].v = (rx.test(nodes[d].name.toLowerCase())) || (rx.test(meshes[nodes[d].meshid].name.toLowerCase())) || ((nodes[d].rnamel != null) && rx.test(nodes[d].rnamel.toLowerCase()));
- }else {
- nodes[d].v = (rx.test(nodes[d].name.toLowerCase())) || ((nodes[d].rnamel != null) && rx.test(nodes[d].rnamel.toLowerCase()));
- }
- } else {
- if(features2 & 0x10000000){
- if (showRealNames) {
- nodes[d].v = (nodes[d].rnamel != null) && rx.test(nodes[d].rnamel.toLowerCase()) || (rx.test(meshes[nodes[d].meshid].name.toLowerCase()));
- } else {
- nodes[d].v = rx.test(nodes[d].name.toLowerCase()) || (rx.test(meshes[nodes[d].meshid].name.toLowerCase()));
- }
- }else{
- if (showRealNames) {
- nodes[d].v = (nodes[d].rnamel != null) && rx.test(nodes[d].rnamel.toLowerCase());
- } else {
- nodes[d].v = rx.test(nodes[d].name.toLowerCase());
- }
- }
- }
- }
- } catch (ex) { for (var d in nodes) { nodes[d].v = true; } }
- }
- // Check power state
- var onlineOnly = Q('OnlineCheckBox').checked;
- if (onlineOnly) { for (var d in nodes) { if ((nodes[d].conn == null) || (nodes[d].conn == 0)) { nodes[d].v = false; } } }
- mainUpdate(4);
- }
- var gotKeyPressEvent = false;
- function ondeskkeypress(e, t) {
- setSessionActivity();
- if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) {
- gotKeyPressEvent = true;
- Q('softKeyboard').value = '';
- // Check what keys we are allows to send
- if (currentNode != null) {
- var meshrights = GetMeshRights(currentNode.meshid);
- var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
- if (inputAllowed == false) return false;
- var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
- if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
- }
- return desktop.m.handleKeys(e);
- }
- if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) {
- if (e.altKey == true) { return true; }
- gotKeyPressEvent = true;
- Q('softKeyboard').value = '';
- var k = 0;
- if (e.charCode != 0) { k = e.charCode; } else if (e.keyCode != 0) { k = e.keyCode; }
- if (k != 0) {
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + String.fromCharCode(k));
- } else {
- // Agent
- terminal.sendText(String.fromCharCode(k));
- }
- }
- return false;
- }
- }
- function ondeskkeydown(e, t) {
- setSessionActivity();
- if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) {
- gotKeyPressEvent = false;
- Q('softKeyboard').value = '';
- // Check what keys we are allows to send
- if (currentNode != null) {
- var meshrights = GetMeshRights(currentNode.meshid);
- var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
- if (inputAllowed == false) return false;
- var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
- if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
- }
- return desktop.m.handleKeyDown(e);
- }
- if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) {
- if (e.altKey == true) { return true; }
- Q('softKeyboard').value = '';
- gotKeyPressEvent = false;
- var k = 0;
- if (e.charCode != 0) { k = e.charCode; } else if (e.keyCode != 0) { k = e.keyCode; }
- if (k == 8) { // Enter and backspace
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + String.fromCharCode(k));
- } else {
- // Agent
- terminal.sendText(String.fromCharCode(k));
- }
- }
- else if (e.ctrlKey && (k >= 64) && (k <= 95)) {
- // Ctrl keys
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + String.fromCharCode(k - 64));
- } else {
- // Agent
- terminal.sendText(String.fromCharCode(k - 64));
- }
- }
- }
- }
- function ondeskkeyup(e, t) {
- setSessionActivity();
- if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) {
- var inputStr = Q('softKeyboard').value;
- Q('softKeyboard').value = '';
- // Check what keys we are allows to send
- if (currentNode != null) {
- var meshrights = GetMeshRights(currentNode.meshid);
- var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
- if (inputAllowed == false) return false;
- var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
- if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
- }
- if ((gotKeyPressEvent == false) && (inputStr.length > 0) && desktop.m.SendKeyUnicode) {
- // This is a mobile keyboard, we need to send that is in the input control.
- var inputchar = inputStr[inputStr.length - 1].charCodeAt(0);
- desktop.m.SendKeyUnicode(desktop.m.KeyAction.DOWN, inputchar);
- desktop.m.SendKeyUnicode(desktop.m.KeyAction.UP, inputchar);
- } else {
- return desktop.m.handleKeyUp(e);
- }
- }
- if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (gotKeyPressEvent == false) && (t !== 1)) {
- if (e.altKey == true) { return true; }
- var inputStr = Q('softKeyboard').value;
- Q('softKeyboard').value = '';
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + inputStr);
- } else {
- // Agent
- if (inputStr)
- terminal.sendText(inputStr);
- }
- return false;
- }
- }
- var sort = 0;
- var deviceHeaderId = 0;
- var deviceHeaderCount;
- var deviceHeaders = {};
- var showRealNames = false;
- var deviceHeaderTotal = 0;
- var deviceHeaders = {};
- var deviceHeadersTitles = {};
- function updateDevices() {
- var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, groups = {}, groupCount = {};
- // 3 wide, list view or desktop view
- deviceHeaderId = 0;
- deviceHeaderCount = {};
- deviceHeaderTotal = 0;
- deviceHeaders = {};
- deviceHeadersTitles = {};
- var current;
- // Perform node sort
- if (sort == 0) { nodes.sort(meshSort); }
- else if (sort == 1) { nodes.sort(powerSort); }
- else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } }
- // Go thru the list of nodes and display them
- for (var i in nodes) {
- if (nodes[i].v == false) continue;
- //var meshrights = GetNodeRights(nodes[i]);
- if (sort == 0) {
- // Mesh header
- nodes.sort(meshSort);
- //if (nodes[i].meshid != current) {
- if (((meshes[nodes[i].meshid]?nodes[i].meshid:'*') != current)) {
- deviceHeaderSet();
- var extra = '';
- if ((meshes[nodes[i].meshid] != null) && (meshes[nodes[i].meshid].mtype == 1)) { extra = '<span style=color:lightgray>' + ", Intel® AMT only" + '</span>'; }
- if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
- r += '<div class=DevSt style=padding-top:4px><span style=float:right>';
- //r += getMeshActions(meshes[nodes[i].meshid], meshrights);
- if (meshes[nodes[i].meshid]) {
- r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
- current = nodes[i].meshid;
- } else {
- r += '</span><span id=MxMESH><i>' + "Individual Devices" + '</i></span><span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
- current = '*';
- }
-
- displayedMeshes[current] = 1;
- c = 0;
- }
- } else if (sort == 1) {
- // Power header
- if (nodes[i].pwr !== current) {
- deviceHeaderSet();
- if (current !== null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
- r += '<div class=DevSt style=width:100%;padding-top:4px><span>' + PowerStateStr2(nodes[i].pwr) + '</span><span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>';
- current = nodes[i].pwr;
- c = 0;
- }
- } else if (sort == 2) {
- // Device header
- if (current == null) { current = '1'; }
- }
- count++;
- r += '<div name=xxdevice onclick=goForward(\'' + nodes[i]._id + '\') class=devList1 id=\'' + nodes[i]._id + '\'></div>'; // This is a standin for the device, it gets rendered only if visible.
- // If we are displaying devices by group, put the device in the right group.
- /*
- if ((sort == 3) && (r != '')) {
- if (nodes[i].tags) {
- for (var j in nodes[i].tags) {
- var tag = nodes[i].tags[j];
- if (groups[tag] == null) { groups[tag] = r; groupCount[tag] = 1; } else { groups[tag] += r; groupCount[tag] += 1; }
- if (view == 3) break;
- }
- }
- r = '';
- }
- */
- deviceHeaderTotal++;
- if (typeof deviceHeaderCount[nodes[i].state] == 'undefined') { deviceHeaderCount[nodes[i].state] = 1; } else { deviceHeaderCount[nodes[i].state]++; }
- }
- // If there is nothing to display, explain the problem
- var viewNothing = false;
- if ((r == '') && (nodes.length > 0) && (Q('SearchInput').value != '')) {
- viewNothing = true;
- r = '<div style="margin:30px">' + "No devices matching this search." + '</div>';
- }
- // Display all empty device groups, we need to do this because users can add devices to these at any time.
- if ((sort == 0) && (Q('SearchInput').value == '')) {
- for (var i in meshes) {
- var mesh = meshes[i];
- if ((displayedMeshes[mesh._id] == null) && (IsMeshViewable(mesh))) {
- if ((current != '') && (r != '')) { r += '</tr></table>'; }
- r += '<div><div colspan=3 class=DevSt><span style=float:right>';
- //r += getMeshActions(mesh, meshrights);
- r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>';
- if (mesh.mtype == 1) { r += '<div style=padding:10px><i>' + "No Intel® AMT devices in this group"; }
- if (mesh.mtype > 1) { r += '<div style=padding:10px><i>' + "No devices in this group"; }
- r += '.</i></div></div>';
- current = mesh._id;
- count++;
- }
- }
- }
- if (count == 0) {
- if ((Q('SearchInput').value != '') || (Q('OnlineCheckBox').checked)) {
- QH('xdevices', '<div style="margin-top:50px;text-align:center"><span style="font-size:30px">' + "No devices" + '</span><br /><br />' + "No devices matching this search." + ' <a onclick=clearSearchInput() style=cursor:pointer>' + "Clear search filter" + '</a></div>');
- } else {
- QH('xdevices', '<div style="margin-top:50px;text-align:center"><span style="font-size:30px">' + "No devices" + '</span><br /><br />' + "Use the desktop version of this website to add devices." + '</div>');
- }
- } else {
- QH('xdevices', r);
- }
- deviceHeaderSet();
- for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
- for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
- onDevicesScrollEx();
- }
- var onDevicesTouchActive = false;
- var onDevicesScrollnagleTimer = null;
- function onDevicesScroll() {
- if (onDevicesScrollnagleTimer == null) { onDevicesScrollnagleTimer = setTimeout(onDevicesScrollEx, 250); }
- }
- function onDeviceTouch(x) {
- if (onDevicesTouchActive == x) return;
- onDevicesTouchActive = x;
- if (x == false) onDevicesScrollEx();
- }
- function onDevicesScrollEx() {
- var devdivs = document.getElementsByName('xxdevice');
- onDevicesScrollnagleTimer = null;
- for (var i = 0; i < devdivs.length; i++) {
- // Show
- var node = getNodeFromId(devdivs[i].id)
- if (node == null) break;
- updateDeviceViewHtml(devdivs[i], node);
- }
- }
- // Update a single device in the current view
- function updateDeviceViewDevice(node) {
- if (node == null) return;
- var devdiv = Q(node._id);
- if ((devdiv != null) && (devdiv.innerHTML != '')) { updateDeviceViewHtml(devdiv, node); } // Only update if the device is visible
- }
- function updateDeviceViewHtml(div, node) {
- var visibleTop = Q('xdevices').scrollTop - 250, visibleBottom = Q('xdevices').scrollTop + Q('xdevices').clientHeight + 250;
- if ((div.offsetTop >= visibleTop) && (div.offsetTop < visibleBottom)) {
- var title = EscapeHtml(node.name);
- if (title.length == 0) { title = '<i>' + "None" + '</i>'; }
- if ((node.rname != null) && (node.rname.length > 0)) { title += ' / ' + EscapeHtml(node.rname); }
- var name = EscapeHtml(node.name);
- if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname);
- if (name.length == 0) { name = '<i>' + "None" + '</i>'; }
- // Add device notification icons
- var devNotify = '', devNotifySub = '';
- // This device is "starred"
- if (stars[node._id] == 1) {
- devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>';
- }
- // This device has session information
- if (node.sessions != null) {
- // Display any agent messages
- if (node.sessions.msg != null) {
- devNotifySub += '<div style="width:16;height:16" class=deviceNotifyDotSub>' + Object.keys(node.sessions.msg).length + '</div>';
- }
- // Sessions are active
- if ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null)) {
- devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>';
- }
- // Help is required
- if (node.sessions.help != null) {
- devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>';
- }
- // Battery state
- if (node.sessions.battery != null) {
- var bat = node.sessions.battery;
- var statestr = '';
- if (bat.state == 'ac') { statestr = "Device is plugged-in"; }
- else if (bat.state == 'dc') { statestr = "Device is battery powered"; }
- var levelstr = '', levelnum = -1;
- if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) {
- levelstr = bat.level + '%';
- levelnum = (Math.floor((bat.level + 10) / 25) + 1);
- if (levelnum > 5) { lvl = 5; }
- if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } }
- }
- if (levelnum > 0) {
- devNotify += '<div class="deviceBatterySmall deviceBatterySmall' + levelnum + '" title="' + ((statestr != null) ? (statestr + ', ' + levelstr) : levelstr) + '"></div>';
- }
- }
- }
- // Add any device icons
- if (devNotifySub != '') { devNotify += '<div class=deviceNotifyDot>' + devNotifySub + '</div>'; }
- // Node
- var icon = node.icon, nodestate = NodeStateStr(node);
- if (((!node.conn) || (node.conn == 0)) && (node.mtype != 3)) { icon += ' gray'; }
- div.innerHTML = '<div>' + devNotify + '<div class="i' + icon + ' devList2"></div><div class=devList3><div class=devList4><b>' + name + '</b></div><div class=devList5>' + nodestate + '</div></div></div>';
- } else {
- div.innerHTML = ''; // Hide
- }
- }
- // Show device help requests
- function showDeviceHelpRequests(nodeid, force, e) {
- if (e) haltEvent(e);
- if (xxdialogMode && !force) return false;
- var node = null, x = '';
- if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); }
- if ((node == null) || (node.sessions == null)) { setDialogMode(0); return false; }
- if (node.sessions.help != null) { for (var j in node.sessions.help) { x += '<div style=margin-bottom:6px><b>' + EscapeHtml(j) + '</b></div><div style=margin-bottom:6px>' + EscapeHtml(node.sessions.help[j]) + '</div>'; } }
- if (x != '') { setDialogMode(2, "Help Requests" + ' - ' + EscapeHtml(node.name), 1, null, x, 'HELPREQ-' + node._id); } else { setDialogMode(0); }
- return false;
- }
- // Show currently active sessions on this device
- function showDeviceSessions(nodeid, force, e) {
- if (((force !== true) && xxdialogMode) || (currentNode == null)) return;
- var node = currentNode, x = '';
- for (var i in node.sessions) {
- if ((i == 'kvm') && (node.sessions.multidesk == null)) {
- x += '<u>' + "Remote Desktop" + '</u>';
- for (var j in node.sessions.kvm) {
- if (j.startsWith('user/')) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
- } else if (j == 'busy') {
- x += addHtmlValue2("Device is busy", ((node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))));
- }
- }
- } else if (i == 'multidesk') {
- x += '<u>' + "Remote Desktop" + '</u>';
- for (var j in node.sessions.multidesk) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("multidesk", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.multidesk[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.multidesk[j]))) + trash);
- }
- } else if (i == 'terminal') {
- x += '<u>' + "Terminal" + '</u>';
- for (var j in node.sessions.terminal) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("terminal", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.terminal[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.terminal[j]))) + trash);
- }
- } else if (i == 'files') {
- x += '<u>' + "Files" + '</u>';
- for (var j in node.sessions.files) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("files", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.files[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.files[j]))) + trash);
- }
- } else if (i == 'tcp') {
- x += '<u>' + "TCP Routing" + '</u>';
- for (var j in node.sessions.tcp) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("tcp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.tcp[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.tcp[j]))) + trash);
- }
- } else if (i == 'udp') {
- x += '<u>' + "UDP Routing" + '</u>';
- for (var j in node.sessions.udp) {
- var trash = '';
- if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("udp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- x += addHtmlValue4(getUserName(j), ((node.sessions.udp[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.udp[j]))) + trash);
- }
- }
- }
- if (x != '') { setDialogMode(2, "Sessions" + ' - ' + EscapeHtml(node.name), 1, null, x, 'SESSIONS-' + node._id); } else { setDialogMode(0); }
- }
- function endDeviceSession(protocol, nodeid, userid) {
- var userIdSplit = decodeURIComponent(userid).split('/'), uid = userIdSplit[0] + '/' + userIdSplit[1] + '/' + userIdSplit[2], guestname = null;
- if ((userIdSplit.length == 4) && (userIdSplit[3].startsWith('guest:'))) { guestname = atob(userIdSplit[3].substring(6)); }
- if (protocol == 'multidesk') {
- meshserver.send({ action: 'endDesktopMultiplex', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname });
- } else {
- meshserver.send({ action: 'msg', type: 'endtunnel', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname, protocol: protocol });
- }
- }
- // Show currently active sessions on this device
- function showDeviceMessages(nodeid, force, e) {
- if (e) haltEvent(e);
- if (xxdialogMode && !force) return false;
- var node = null, x = '<div style=max-height:200px;width:100%;overflow-y:auto;overflow-x:hidden>', count = 0;
- if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); }
- if ((node == null) || (node.sessions == null) || (node.sessions.msg == null)) { setDialogMode(0); return false; }
- for (var i in node.sessions.msg) {
- var msg = i, icon = 5;
- if (typeof node.sessions.msg[i].msg == 'string') { msg = node.sessions.msg[i].msg; }
- if (typeof node.sessions.msg[i].icon == 'number') { icon = node.sessions.msg[i].icon; }
- if ((icon < 1) || (icon > 9)) { icon = 5; }
- x += '<table style=width:100%><td style=width:24px><div class=NotifyIconSmall' + icon + '></div><td><div style="border-radius:5px;background-color:#BBB;width:calc(100% - 18px);padding:8px">' + EscapeHtml(msg) + '</div></table>';
- count++;
- }
- x += '</div>';
- if (count > 0) setDialogMode(2, "Agent Messages" + ' - ' + EscapeHtml(node.name), 1, null, x, 'MESSAGES-' + node._id);
- return false;
- }
- var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present", "Off"];
- var powerStateStrings = ['', "Powered", "Sleeping", "Sleeping", "Deep Sleep", "Hibernating", "Soft-Off", "Present", "Off"];
- var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined", "The device is powered off"];
- var powerColorTable = ['#00000000', 'black', 'blue', 'blue', 'lightblue', 'blueviolet', 'darkgreen', 'lightseagreen', 'lightseagreen'];
- function NodeStateStr(node) {
- var states = [];
- if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]);
- if (node.conn) {
- if ((node.conn & 1) != 0) { states.push('<span>' + ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Agent") + '</span>'); }
- if ((node.conn & 2) != 0) { states.push('<span>' + "CIRA" + '</span>'); }
- else if ((node.conn & 4) != 0) { states.push('<span>' + "Intel® AMT" + '</span>'); }
- if ((node.conn & 8) != 0) { states.push('<span>' + "Relay" + '</span>'); }
- if ((node.conn & 16) != 0) { states.push('<span>' + "MQTT" + '</span>'); }
- }
- if ((node.pwr != null) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); }
- return states.join(', ');
- }
- function PowerStateStr(x) {
- if (x < powerStatetable.length) return powerStatetable[x];
- return '';
- }
- function PowerStateStr2(x) {
- if ((x != 0) && (x < powerStatetable.length)) return powerStatetable[x];
- return "Unknown";
- }
- function onSortSelectChange(skipsave) {
- sort = document.getElementById('sortselect').selectedIndex;
- if (!skipsave) { putstore('sort', sort); }
- mainUpdate(4);
- }
- function deviceHeaderSet() {
- if (deviceHeaderId == 0) { deviceHeaderId = 1; return; }
- deviceHeaders['DevxHeader' + deviceHeaderId] = ', ' + deviceHeaderTotal + ((deviceHeaderTotal == 1) ? " node" : " nodes");
- var title = '';
- for (var x in deviceHeaderCount) { if (title.length > 0) title += ', '; title += deviceHeaderCount[x] + ' ' + PowerStateStr2(x); }
- deviceHeadersTitles['DevxHeader' + deviceHeaderId] = title;
- deviceHeaderId++;
- deviceHeaderCount = {};
- deviceHeaderTotal = 0;
- }
- /*
- function meshSort(a, b) { if (a.meshnamel > b.meshnamel) return 1; if (a.meshnamel < b.meshnamel) return -1; if (a.meshid == b.meshid) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; }
- function powerSort(a, b) { var ap = a.pwr ? a.pwr : 0; var bp = b.pwr ? b.pwr : 0; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } if (ap > bp) return 1; if (ap < bp) return -1; return 0; }
- function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; }
- function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; }
- */
- var sortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })
- function meshSort(a, b) {
- var x = sortCollator.compare(a.meshnamel, b.meshnamel);
- if (x != 0) return x;
- x = sortCollator.compare(a.meshid, b.meshid);
- if (x != 0) return x;
- if (showRealNames == true) { return sortCollator.compare(a.rnamel, b.rnamel); }
- return sortCollator.compare(a.namel, b.namel);
- }
- function powerSort(a, b) { var ap = a.pwr ? a.pwr : 0; var bp = b.pwr ? b.pwr : 0; if (ap > bp) return -1; if (ap < bp) return 1; if (ap == bp) { if (showRealNames == true) { return sortCollator.compare(a.rnamel, b.rnamel); } else { return sortCollator.compare(a.namel, b.namel); } } }
- function deviceSort(a, b) { return sortCollator.compare(a.namel, b.namel); }
- function deviceHostSort(a, b) { return sortCollator.compare(a.rnamel, b.rnamel); }
- //
- // MY DEVICE
- //
- function refreshDevice(nodeid) {
- if (!currentNode || currentNode._id != nodeid) return;
- gotoDevice(nodeid, xxcurrentView, true);
- }
- var currentDevicePanel = 0;
- var currentNode;
- var powerTimelineNode = null;
- var powerTimelineReq = null;
- var powerTimelineUpdate = null;
- var powerTimeline = null;
- function getCurrentNode() { return currentNode; };
- function gotoDevice(nodeid, panel, refresh) {
- // Remind the user to verify the email address
- if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; }
- // Remind the user to add two factor authentication
- if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || (userinfo.otpduo > 0) || (userinfo.otpdev > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; }
- var node = getNodeFromId(nodeid);
- if (node == null) { goBack(); return; }
- var mesh = meshes[node.meshid];
- var meshrights = GetNodeRights(node);
- var deviceSwitch = ((currentNode == null) || (currentNode._id != nodeid));
- if (!currentNode || currentNode._id != node._id || refresh == true) {
- currentNode = node;
- // Setup session notification
- QV('p10deviceNotify', (currentNode.sessions != null) && ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null)));
- QV('p10deviceStar', stars[currentNode._id] == 1);
- QV('p10deviceHelp', (currentNode.sessions != null) && (currentNode.sessions.help != null))
- if ((currentNode.sessions != null) && (currentNode.sessions.msg != null)) { QV('p10deviceMsg', true); QH('p10deviceMsg', Object.keys(currentNode.sessions.msg).length); } else { QV('p10deviceMsg', false); }
- // Device Battery
- QV('p10deviceBattery', false);
- if ((currentNode.sessions != null) && (currentNode.sessions.battery != null)) {
- var bat = currentNode.sessions.battery;
- var statestr = '';
- if (bat.state == 'ac') { statestr = "Device is plugged-in"; }
- if (bat.state == 'dc') { statestr = "Device is battery powered"; }
- var levelstr = '', levelnum = -1;
- if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) {
- levelstr = bat.level + '%';
- levelnum = (Math.floor((bat.level + 10) / 25) + 1);
- if (levelnum > 5) { lvl = 5; }
- if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } }
- }
- if (levelnum > 0) {
- Q('p10deviceBattery').title = (statestr != null) ? (statestr + ', ' + levelstr) : levelstr;
- QV('p10deviceBattery', true);
- Q('p10deviceBattery').className = 'deviceBatteryLarge deviceBatteryLarge' + levelnum;
- }
- } else {
- QV('p10deviceBattery', false);
- }
- // Add node name
- var nname = EscapeHtml(node.name);
- if (nname.length == 0) { nname = '<i>' + "None" + '</i>'; }
- if ((meshrights & 4) != 0) { nname = '<span onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + '</span>'; }
- QH('p10deviceName', nname);
- // Node attributes
- var x = '<table style=width:100%>';
- // Attribute: Mesh
- if (mesh) { x += addDeviceAttribute('<span>' + "Group" + '</span>', '<a onclick=goForward("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>'); }
- // Attribute: Name
- if (node.rname != null) { x += addDeviceAttribute('<span>' + "Name" + '</span>', '<span>' + EscapeHtml(node.rname) + '</span>'); }
- // Attribute: Host
- if ((((features & 1) == 0) && (node.mtype != 4)) || (node.mtype == 3)) { // If not WAN-only, local hostname is in use
- if ((meshrights & 4) != 0) {
- if (node.host) {
- x += addDeviceAttribute("Hostname", '<span onclick=showEditNodeValueDialog(1) style=cursor:pointer>' + EscapeHtml(node.host) + '</span>');
- } else {
- x += addDeviceAttribute("Hostname", '<span onclick=showEditNodeValueDialog(1) style=cursor:pointer><i>' + "None" + '</i></span>');
- }
- } else {
- x += addDeviceAttribute("Hostname", EscapeHtml(node.host));
- }
- }
- // Attribute: Description
- var description = node.desc ? EscapeHtml(node.desc) : '<i>' + "None" + '</i>';
- if ((meshrights & 4) != 0) {
- x += addDeviceAttribute("Description", '<span onclick=showEditNodeValueDialog(2) style=cursor:pointer>' + description + '</span>');
- } else {
- x += addDeviceAttribute("Description", description);
- }
- // IP-KVM information
- if (node.mtype == 4) {
- if (node.portnum != null) { x += addDeviceAttribute("Port Number", node.portnum); }
- if (node.porttype != null) { x += addDeviceAttribute("Port Type", node.porttype); }
- }
- // Attribute: Mesh Agent
- if ((node.agent != null) && (node.agent.id != null) && (node.mtype == 3)) {
- if (node.agent.id == 4) { x += addDeviceAttribute("Device Type", "Windows"); }
- if (node.agent.id == 6) { x += addDeviceAttribute("Device Type", "Linux"); }
- if (node.agent.id == 29) { x += addDeviceAttribute("Device Type", "macOS"); }
- } else if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
- var str = '';
- if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
- if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
- if (node.agent.id == 14) { str = node.agent.core; }
- if ((node.agent.root === false) && ((node.conn & 1) != 0)) { str += ', ' + "Restricted"; }
- x += addDeviceAttribute("Mesh Agent", str);
- }
- // Attribute: Intel AMT
- if (node.intelamt != null) {
- var str = '';
- var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") };
- if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>' + nobreak("Unknown State") + '</i>, v' + EscapeHtml(node.intelamt.ver); }
- else if ((node.intelamt.ver == null) && (node.intelamt.state == 2)) { str += '<i>' + "Activated" + '</i>'; }
- else if ((node.intelamt.ver == null) || (node.intelamt.state == null)) { str += '<i>' + "Unknown Version & State" + '</i>'; }
- else {
- str += provisioningStates[node.intelamt.state];
- if (node.intelamt.flags) { if (node.intelamt.flags & 2) { str = ' <span>' + "CCM" + '</span>'; } else if (node.intelamt.flags & 4) { str = ' <span>' + "ACM" + '</span>'; } }
- str += (', v' + EscapeHtml(node.intelamt.ver));
- }
- // If Intel AMT is activated, show additional options
- if (node.intelamt.state == 2) {
- if (node.intelamt.tls == 1) { str += ', <span title="' + "Intel® AMT is setup with TLS network security" + '">' + "TLS" + '</span>'; }
- var editUserCredentialsIcon = false;
- if (node.intelamt.user == null || node.intelamt.user == '') { // If credentials are not set, allow setting them.
- if ((meshrights & 4) != 0) {
- str += ', <i style=color:#FF0000;cursor:pointer title="' + "Edit Intel® AMT credentials" + '" onclick=editDeviceAmtSettings("' + node._id + '")>' + "No Credentials" + '</i>';
- editUserCredentialsIcon = true;
- } else {
- str += ', <i style=color:#FF0000>' + "No Credentials" + '</i>';
- }
- } else if (((features2 & 1) != 0) && (node.intelamt.warn != null)) { // If AMT manager is running and warned of invalid credentials, allow setting them.
- var warn = null;
- if ((node.intelamt.warn & 1) != 0) { warn = "Invalid Credentials"; }
- if ((node.intelamt.warn & 8) != 0) { warn = "Trying Credentials"; }
- if (warn != null) {
- if ((meshrights & 4) != 0) {
- str += ', <i style=color:#FF0000;cursor:pointer title="' + "Edit Intel® AMT credentials" + '" onclick=editDeviceAmtSettings("' + node._id + '")>' + warn + '</i>';
- editUserCredentialsIcon = true;
- } else {
- str += ', <i style=color:#FF0000>' + warn + '</i>';
- }
- }
- }
- // If the AMT manager is not running, always allow Intel AMT credentials to be edited.
- if (((meshrights & 4) != 0) && ((features2 & 1) == 0)) { editUserCredentialsIcon = true; }
- str += ' ';
- if (editUserCredentialsIcon) {
- str += '<img src=images/link4.png height=10 width=10 title="' + "Edit Intel® AMT credentials" + '" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
- }
- }
- /*
- if (node.intelamt.state == 2) {
- if (node.intelamt.user == null || node.intelamt.user == '') {
- if ((meshrights & 4) != 0) {
- str += ', <i style=color:#FF0000;cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>' + nobreak("No Credentials") + '</i>';
- } else {
- str += ', <i style=color:#FF0000>' + "No Credentials" + '</i>';
- }
- }
- str += ' ';
- if ((meshrights & 4) != 0) {
- str += '<img src=images/link4.png height=10 width=10 style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
- }
- }
- */
- var meName = "Intel® ME";
- if (typeof node.intelamt.sku == 'number') {
- if ((node.intelamt.sku & 8) != 0) { meName = "Intel® AMT"; }
- else if ((node.intelamt.sku & 16) != 0) { meName = "Intel® SM"; }
- }
- x += addDeviceAttribute(meName, str);
- }
- // Attribute: Mesh Agent Tag
- if ((node.agent != null) && (node.agent.tag != null) && (node.agent.tag != 'mailto:')) {
- var tag = EscapeHtml(node.agent.tag);
- if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; }
- x += addDeviceAttribute("Agent Tag", tag);
- }
- // Attribute: Intel AMT
- //if (node.intelamt && node.intelamt.user) { x += addDeviceAttribute('Intel® AMT', node.intelamt.user); }
- // Attribute: Connectivity (Only show this if more than just the agent is connected).
- var connectivity = node.conn;
- if (connectivity && connectivity > 1) {
- var cstate = [];
- if ((node.conn & 1) != 0) cstate.push('<span>' + ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Agent") + '</span>');
- if ((node.conn & 2) != 0) cstate.push('<span>' + "Intel® AMT CIRA" + '</span>');
- else if ((node.conn & 4) != 0) cstate.push('<span>' + "Intel® AMT" + '</span>');
- if ((node.conn & 8) != 0) cstate.push('<span>' + "Agent Relay" + '</span>');
- if ((node.conn & 16) != 0) cstate.push('<span>' + "MQTT" + '</span>');
- x += addDeviceAttribute("Connectivity", cstate.join(', '));
- }
- // Node tags
- var groupingTags = '<i>' + "None" + '</i>';
- if (node.tags != null) { groupingTags = ''; for (var i in node.tags) { groupingTags += '<span class=tagSpan>' + EscapeHtml(node.tags[i]) + '</span> '; } }
- if ((meshrights & 4) != 0) {
- x += addDeviceAttribute("Tags", '<span onclick=showEditNodeValueDialog(3) style=cursor:pointer;color:black>' + groupingTags + '</span>');
- } else {
- x += addDeviceAttribute("Tags", '<span style=line-height:26px;color:black>' + groupingTags + '</span>');
- }
- // SSH & RDP Credentials
- if ((node.ssh != null) || (node.rdp != null)) {
- var y = [];
- if ((meshrights & 4) != 0) {
- if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 1) ? "SSH-User+Pass" : ((node.ssh == 2) ? "SSH-User+Key+Pass" : "SSH-User+Key")) + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
- if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); }
- } else {
- if (node.ssh != null) { y.push(((node.ssh == 1) ? "SSH-User+Pass" : ((node.ssh == 2) ? "SSH-User+Key+Pass" : "SSH-User+Key"))); }
- if (node.rdp != null) { y.push("RDP"); }
- }
- x += addDeviceAttribute("Credentials", y.join(', '));
- }
- x += '</table><br />';
- // Show action button, only show if we have permissions 4, 8, 64
- if (((meshrights & (4 + 8 + 64 + 262144)) != 0) && (node.mtype < 3)) { x += '<input type=button value="' + "Actions" + '" onclick=deviceActionFunction() />'; }
- x += '<input type=button value="' + "Notes" + '" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
- //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast onclick=deviceToastFunction() />'; }
- if ((node.mtype == 4) && (connectivity & 1)) {
- if (node.porttype == 'PDU') {
- if (node.pwr == 1) {
- if (meshrights & 0x40000) { x += '<input type=button value="' + "Turn off" + '" title="' + "Turn off" + '" onclick=setIpPduState(0) />'; }
- } else if (node.pwr == 8) {
- if (meshrights & 0x40) { x += '<input type=button value="' + "Turn on" + '" title="' + "Turn on" + '" onclick=setIpPduState(1) />'; }
- }
- } else {
- if (meshrights & 8) { x += '<input type=button value="' + "Remote Control" + '" title="' + "Remote Control" + '" onclick=openIpKvmRemoteControl("' + encodeURIComponentEx(node._id) + '") />'; }x
- }
- }
- QH('p10html', x);
- // If we are looking at a local non-windows device, enable terminal and files capability.
- if ((node.mtype == 3) && (node.agent != null) && (node.agent.id > 4) && (features2 & 0x00000200)) { node.agent.caps = 6; }
- // Show node last 7 days timeline
- //drawDeviceTimeline();
- setupTerminal();
- setupFiles();
- if (meshrights & 16) { setupConsole(); }
- // Show bottom buttons
- x = '<div style=float:right;font-size:x-small;margin-right:10px>';
- if ((meshrights & 0x8000) != 0) { x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '")>' + "Delete Device" + '</a>'; }
- x += '</div><div style=font-size:x-small>';
- if (webRelayPort != 0) {
- x += '<a onclick=p10WebRouter("' + node._id + '",1,' + (node.httpport ? node.httpport : 80) + ')>' + "HTTP" + ((node.httpport && (node.httpport != 80)) ? '/' + node.httpport : '') + '</a> ';
- x += '<a onclick=p10WebRouter("' + node._id + '",2,' + (node.httpsport ? node.httpsport : 443) + ')>' + "HTTPS" + ((node.httpsport && (node.httpsport != 443)) ? '/' + node.httpsport : '') + '</a> ';
- }
- // noVNC link
- if ((((connectivity & 1) != 0) || (node.mtype == 3)) && (node.agent) && ((meshrights & 8) != 0) && ((features & 0x20000000) == 0)) {
- x += '<a id=rfbLink onclick=p10rfb("' + node._id + '")>' + "Web-VNC" + '</a> ';
- }
- // MSTSC.js link
- if ((((connectivity & 1) != 0) || (node.mtype == 3)) && (node.agent) && ((meshrights & 8) != 0) && ((features & 0x40000000) == 0)) {
- x += '<a id=mstscLink onclick=p10mstsc("' + node._id + '")>' + "Web-RDP" + '</a> ';
- }
- // SSH link
- if ((features2 & 0x200) && (((connectivity & 1) != 0) || (node.mtype == 3)) && (node.agent) && ((meshrights & 8) != 0)) {
- x += '<a id=sshLink onclick=p10ssh("' + node._id + '")>' + "Web-SSH" + '</a> ';
- }
- //if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '")>Interfaces</a> ';
- //if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '")>Location</a> ';
- x += '</div><br>'
- QH('p10html3', x);
- // Set the node power state
- var powerstate = PowerStateStr(node.state);
- //if (node.state == 0) { powerstate = 'Unknown State'; }
- if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += ((node.mtype == 4) ? ((node.porttype == 'PDU') ? "Switch" : "IP-KVM") : "Mesh Agent"); }
- if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT connected"; }
- else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT detected"; }
- if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "MQTT channel connected"; }
- if ((node.porttype == 'PDU') || ((node.pwr > 1) && (node.pwr != 7))) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += powerStateStrings[node.pwr]; }
- QH('MainComputerState', '<span style=font-size:12px>' + powerstate + '</span>');
- // Set the node icon
- var icon = node.icon;
- if (((!node.conn) || (node.conn == 0)) && (node.mtype != 3)) { icon += ' gray'; }
- QH('MainComputerImage', '<div class="i' + icon + '"></div>');
- // Request the power timeline
- if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) {
- QH('p10html2', '');
- powerTimelineReq = currentNode._id;
- meshserver.send({ action: 'powertimeline', nodeid: currentNode._id });
- meshserver.send({ action: 'lastconnect', nodeid: currentNode._id });
- meshserver.send({ action: 'getsysinfo', nodeid: currentNode._id });
- meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id });
- QH('p10detailshtml', '');
- }
- // Clear user consent status if present
- if (deviceSwitch) {
- p11clearConsoleMsg();
- p13clearConsoleMsg();
- }
- // Clear the desktop session selector
- QV('p11DeskSessionSelector', false);
- QH('p11DeskSessionSelector', '');
- }
- setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
- if (!panel) panel = 10;
- go(panel);
- // Update the footer menu
- if (xxcurrentView == 10) { setupDeviceMenu(); }
- }
- function setIpPduState(op) {
- if (op == 0) {
- setDialogMode(2, "Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: 2 }); }, "Perform power off?"); // Turn off
- } else {
- setDialogMode(2, "Power Operation", 3, function () { meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] }); }, "Perform power on?"); // Turn on
- }
- }
- function openIpKvmRemoteControl(nodeid) {
- if (xxdialogMode) return;
- var nid = decodeURIComponent(nodeid).split('/')[2];
- safeNewWindow('/ipkvm.ashx/' + nid + '/', 'ipkvm:' + nid);
- }
- function deviceToastFunction() {
- if (xxdialogMode) return;
- setDialogMode(2, "Device Toast", 3, deviceToastFunctionEx, '<textarea id=d2devToast style=width:100%;height:80px;resize:none;overflow-y:scroll></textarea>');
- }
- function deviceToastFunctionEx() {
- meshserver.send({ action: 'toast', nodeids: [currentNode._id], title: 'MeshCentral', msg: Q('d2devToast').value });
- }
- // && ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0))
- function setupDeviceMenu(op, obj) {
- var meshrights = GetNodeRights(currentNode);
- if (op != null) { currentDevicePanel = op; }
- QV('p10general', currentDevicePanel == 0);
- QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
- QV('p10files', currentDevicePanel == 2);
- QV('p10details', currentDevicePanel == 3);
- QV('p10console', currentDevicePanel == 4);
- QV('p10terminal', currentDevicePanel == 5);
- var menus = [];
- if (currentDevicePanel != 0) { menus.push({ n: "General", f: 'setupDeviceMenu(0)' }); }
- if ((currentDevicePanel != 1) &&
- (currentNode != null) &&
- ((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0)) &&
- (((currentNode.agent == null) && (currentNode.intelamt) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 1)))
- ) { menus.push({ n: "Desktop", f: 'setupDeviceMenu(1)' }); }
- if ((currentDevicePanel != 5) &&
- (currentNode != null) &&
- ((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0)) &&
- (((currentNode.agent == null) && (currentNode.intelamt) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 2)))
- ) { menus.push({ n: "Terminal", f: 'setupDeviceMenu(5)' }); }
- if ((currentDevicePanel != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype != 1) && (currentNode.agent) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); }
- if ((currentDevicePanel != 3) && (currentNode != null) && (currentNode.mtype < 3) && ((meshrights & 1048576) != 0)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); }
- if ((currentDevicePanel != 4) && (currentNode != null) && (meshrights & 0x00000010) && (currentNode.mtype == 2)) { menus.push({ n: "Console", f: 'setupDeviceMenu(4)' }); }
- updateFooterMenu(menus);
- updateCurrentUrl();
- if (currentDevicePanel == 1) { deskAdjust(); }
- }
- function deviceActionFunction() {
- if (xxdialogMode) return;
- var rights = GetNodeRights(currentNode), count = 0;
- var x = "Select an operation to perform on this device." + '<br /><br />';
- var y = '<select id=d2deviceop style=float:right;width:170px onchange=deviceActionFunctionValidate()>';
- var z = '';
- if ((currentNode.agent != null) && (currentNode.agent.id == 14)) {
- if (((currentNode.conn & 1) != 0) && ((rights & 8) != 0)) {
- count++;
- y += '<option value=400>' + "Flash" + '</option>';
- y += '<option value=401>' + "Vibrate" + '</option>';
- z += '<div id=d2devicetimediv>' + addHtmlValue("Time", '<select id=d2devicetime style=float:right;width:170px><option value=1000>' + "1 second" + '</option><option value=5000>' + "5 seconds" + '</option><option value=10000>' + "10 seconds" + '</option></select>') + '</div>';
- }
- } else {
- if ((rights & 64) != 0) { count++; y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
- //if (((currentNode.conn & 1) != 0) && ((rights & 131072) != 0)) { count++; y += '<option value=106>' + "Run Commands" + '</option>'; } // Remote command permission
- if ((currentNode.conn != 0) && ((rights & 262144) != 0)) { count++; y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; }
- //if ((currentNode.conn & 16) != 0) { count++; y += '<option value=103>' + "Send MQTT Message" + '</option>'; }
- if ((currentNode.intelamt != null) && (currentNode.intelamt.state == 2) && ((currentNode.conn & 6) != 0) && ((rights & 262144) != 0)) {
- count++;
- y += '<option value=310>' + "Intel® AMT Reset" + '</option>';
- y += '<option value=308>' + "Intel® AMT Power off" + '</option>';
- }
- if ((currentNode.intelamt != null) && (currentNode.intelamt.state == 2) && ((currentNode.conn & 6) != 0) && ((rights & 64) != 0)) {
- count++;
- y += '<option value=302>' + "Intel® AMT Power on" + '</option>';
- }
- //if ((getNodeAmtVersion(currentNode) >= 15) && (currentNode.intelamt.state == 2) && ((currentNode.conn & 6) != 0) && (rights == 0xFFFFFFFF) && ((features & 0x00000400) == 0)) { count++; y += '<option value=107>' + "Intel® AMT One Click Recovery" + '</option>'; } // CIRA (2) or AMT (4) connected
- //if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { count++; y += '<option value=104>' + "Uninstall Agent" + '</option>'; }
- }
- y += '</select>';
- x += addHtmlValue("Operation", y);
- if (count == 0) { x = "No actions currently available for this device."; }
- setDialogMode(2, "Device Action", (count == 0) ? 2 : 3, deviceActionFunctionEx, x + z);
- if (count > 0) { deviceActionFunctionValidate(); }
- }
- function deviceActionFunctionValidate() {
- var op = Q('d2deviceop').value;
- try { QV('d2devicetimediv', (op == 400) || (op == 401)); } catch (ex) { }
- }
- function deviceActionFunctionEx() {
- var op = Q('d2deviceop').value;
- if (op == 100) {
- // Device wake
- meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] });
- } else if (op == 103) {
- // Send MQTT Message
- //p10showSendMqttMsgDialog([currentNode._id]);
- } else if (op == 104) {
- // Uninstall agent
- //p10showSendUninstallAgentDialog([currentNode._id]);
- } else if (op == 106) {
- // Run commands
- /*
- var wintype = false, linuxtype = false;
- if (currentNode.agent) { if ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) { wintype = true; } else { linuxtype = true; } }
- if ((wintype == true) || (linuxtype == true)) {
- var x = "Run commands on selected devices." + '<br />';
- if (wintype == true) {
- x += '<select id=d2cmdtype style=width:100%;margin-bottom:4px;margin-top:4px>';
- x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>';
- if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
- x += '</select>';
- }
- x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>';
- x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
- setDialogMode(2, "Run Commands", 3, deviceRunCmdsFunctionEx, x);
- Q('d2runcmd').focus();
- //QE('idx_dlgOkButton', true);
- }
- */
- } else if (op == 107) {
- // Intel AMT One Click Recovery (OCR)
- /*
- Q('d3localmodeform').action = 'oneclickrecovery.ashx';
- Q('d3auth').value = authCookie;
- Q('d3filter').value = '.iso';
- Q('d3attrib').value = currentNode._id;
- setDialogMode(3, "Intel® AMT One Click Recovery", 3, deviceActionOneClickRecovery);
- d3init();
- */
- } else if (op == 302) { // Intel AMT power on
- setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT power on?");
- } else if (op == 308) { // Intel AMT power off
- setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT power off?<br><br><b>NOTE: If there is an active AMT session, then power off command will be rejected, so you must disconnect from the AMT session first!</b>");
- } else if (op == 310) { // Intel AMT reset
- setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT reset?");
- } else if ((op == 400) || (op == 401)) {
- // Flash / vibrate
- meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op), time: parseInt(Q('d2devicetime').value) });
- } else {
- // Power operation
- meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) });
- }
- }
- function showNotes(readonly, noteid) {
- if (xxdialogMode) return;
- if (noteid == null) { noteid = encodeURIComponentEx('p' + userinfo._id); }
- var x = '<textarea id=d2devNotes ro=' + readonly + ' noteid=' + noteid + ' readonly style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>';
- if (noteid.startsWith('node%2F%2F')) { x += '<span style=font-size:10px>' + "Device group notes can be viewed and changed by other device group administrators." + '<span>'; }
- setDialogMode(2, "Notes", 3, showNotesEx, x, noteid);
- meshserver.send({ action: 'getNotes', id: decodeURIComponent(noteid) });
- }
- function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponentEx(Q('d2devNotes').value) }); }
- function deviceLockFunction() {
- if ((xxdialogMode != null || xxdialogMode == 0) && (desktop != null) && (desktop.contype == 1)) { setDialogMode(2, "Lock Desktop", 3, function() { if ((desktop != null) && (desktop.contype == 1)) { desktop.sendCtrlMsg('{"ctrlChannel":"102938","type":"lock"}'); } }, "Lock user desktop?"); }
- }
- function deviceChat(e) {
- if (xxdialogMode) return;
- setDialogMode(2, "Device Action", 3, function () {
- var url = '/messenger?id=meshmessenger/' + encodeURIComponentEx(currentNode._id) + '/' + encodeURIComponentEx(userinfo._id) + '&title=' + currentNode.name;
- if (serverinfo.domainsuffix != '') { url = '/' + serverinfo.domainsuffix + url; }
- if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
- if (e && (e.shiftKey == true)) {
- safeNewWindow(url, 'meshmessenger:' + currentNode._id);
- } else {
- safeNewWindow(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560');
- }
- meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) });
- }, "Start chat session?");
- }
- function deviceUrlFunction() {
- if (xxdialogMode) return;
- setDialogMode(2, "Open Page on Device", 3, deviceUrlFunctionEx, '<input id=d2devurl placeholder="http://server.com" style=width:100%;overflow-y:scroll onkeyup=deviceUrlFunctionValidate() onchange=deviceUrlFunctionValidate()></input>');
- Q('d2devurl').focus();
- deviceUrlFunctionValidate();
- }
- function deviceUrlFunctionValidate() {
- var x = Q('d2devurl').value.toLowerCase();
- QE('idx_dlgOkButton', ((x.startsWith('http://') && (x.length > 7)) || (x.startsWith('https://') && (x.length > 8))));
- }
- function deviceUrlFunctionEx() {
- meshserver.send({ action: 'msg', type: 'openUrl', nodeid: currentNode._id, url: Q('d2devurl').value });
- }
- function runDeviceCmd(nodeid) { if (xxdialogMode) return; d2runCommandDialog({ nodeids: [ nodeid ? decodeURIComponent(nodeid) : currentNode._id ] }); }
- function d2runCommandDialog(options) {
- var wintype = false, linuxtype = false, agenttype = false;
- for (var i in options.nodeids) {
- var n = getNodeFromId(options.nodeids[i]);
- if (n.agent) { if ((GetNodeRights(n) & 24) == 24) { agenttype = true; }
- if (isWindowsNode(n)) { wintype = true; } else { linuxtype = true; } }
- }
- if ((wintype == true) || (linuxtype == true) || (agenttype == true)) {
- // Fetch run options
- var runopt = { type:1, runAs:0, source:1, cmd:'' };
- try { runopt = JSON.parse(getstore('runopt', runopt)); } catch (ex) {}
- if (options.selectedFile) {
- var filename = options.selectedFile.name.toLowerCase();
- console.log('filename', filename);
- if (filename.endsWith('.bat')) { runopt.type = 1; }
- if (filename.endsWith('.ps1')) { runopt.type = 2; }
- if (filename.endsWith('.sh')) { runopt.type = 3; }
- if (filename.endsWith('.agentconsole')) { runopt.type = 4; }
- }
- var x = '';
- if (options.title) { x += options.title + '<br />'; }
- x += '<select id=d2cmdtype onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px;margin-top:4px>';
- if (wintype == true) { x += '<option value=1' + ((runopt.type == 1)?' selected':'') + '>' + "Windows Command Prompt" + '</option><option value=2' + ((runopt.type == 2)?' selected':'') + '>' + "Windows PowerShell" + '</option>'; }
- if (linuxtype == true) { x += '<option value=3' + ((runopt.type == 3)?' selected':'') + '>' + "Linux/BSD/macOS Command Shell" + '</option>'; }
- if (agenttype == true) { x += '<option value=4' + ((runopt.type == 4)?' selected':'') + '>' + "Agent Console" + '</option>'; } // MESHRIGHT_REMOTECONTROL & MESHRIGHT_AGENTCONSOLE are needed
- x += '</select>';
- x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0' + ((runopt.runAs == 0)?' selected':'') + '>' + "Run as agent" + '</option><option value=1' + ((runopt.runAs == 1)?' selected':'') + '>' + "Run as user, agent if no user" + '</option><option value=2' + ((runopt.runAs == 2)?' selected':'') + '>' + "Must run as user" + '</option></select>';
- if (options.selectedFile == null) {
- x += '<select id=d2cmdsource onclick=d2runCommandValidate() style=width:100%;margin-bottom:4px><option value=0' + ((runopt.source == 0)?' selected':'') + '>' + "Commands from text box" + '</option><option value=1' + ((runopt.source == 1)?' selected':'') + '>' + "Commands from file" + '</option>';
- if (userinfo.siteadmin & 8) { x += '<option value=2' + ((runopt.source == 2)?' selected':'') + '>' + "Commands from file on server" + '</option>'; }
- x += '</select><textarea id=d2runcmd onkeyup=d2runCommandValidate() style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll>' + (runopt.cmd ? EscapeHtml(decodeURIComponent(runopt.cmd)) : '') + '</textarea>';
- x += '<div id=d2runfile style=display:none><input id=d2runfileex type=file onchange=d2runCommandValidate() id=d2localFile name=files onchange=d2runCommandValidate() /></div>';
- if (userinfo.siteadmin & 8) { x += '<div id=d2runsfile style=display:none><div id=d2serveraction valign=bottom><input type=button id=p2FolderUp disabled="disabled" onclick=d3folderup() value="Up" /> <span id=p2CurrentFolder></span></div><div id=d2serverfiles></div></div>'; }
- }
- setDialogMode(2, "Run Commands", 3, d2groupActionFunctionRunCommands, x, options);
- if (options.selectedFile == null) {
- Q('d2runcmd').focus();
- if (userinfo.siteadmin & 8) { d3fileoptions = { dialog: 2, files: 'd2serverfiles', folderup: 'p2FolderUp', currentFolder: 'p2CurrentFolder', func: null }; d3updatefiles(); } // Update the server files
- }
- d2runCommandValidate();
- }
- }
- function d2runCommandValidate() {
- QV('d2cmduser', Q('d2cmdtype').value < 4);
- if (xxdialogTag.selectedFile == null) {
- QV('d2runcmd', Q('d2cmdsource').value == 0);
- QV('d2runfile', Q('d2cmdsource').value == 1);
- QV('d2runsfile', Q('d2cmdsource').value == 2);
- var ok = false;
- if (Q('d2cmdsource').value == 0) { if (Q('d2runcmd').value.length > 0) { ok = true; } } // From text box
- if (Q('d2cmdsource').value == 1) { if (Q('d2runfileex').files.length == 1) { ok = true; } } // From file
- if (Q('d2cmdsource').value == 2) { ok = false; } // From server file
- QE('idx_dlgOkButton', ok);
- } else {
- QE('idx_dlgOkButton', true);
- }
- }
- function d2groupActionFunctionRunCommands(b, options) {
- var type = 3;
- try { type = parseInt(Q('d2cmdtype').value); } catch (ex) { }
- if (options.selectedFile == null) { putstore('runopt', JSON.stringify({ type: type, runAs: parseInt(Q('d2cmduser').value), source: parseInt(Q('d2cmdsource').value), cmd: encodeURIComponent(Q('d2runcmd').value) })); } // Save run options
- var cmd = { action: 'runcommands', nodeids: options.nodeids, type: type, runAsUser: parseInt(Q('d2cmduser').value) };
- if (options.selectedFile) {
- // Drag & drop file
- var reader = new FileReader();
- reader.onload = function (e) { cmd.cmds = e.target.result; meshserver.send(cmd); if (options.func) { options.func(); } }
- reader.readAsText(options.selectedFile);
- } else if (Q('d2cmdsource').value == 0) {
- // From text box
- cmd.cmds = Q('d2runcmd').value;
- meshserver.send(cmd);
- if (options.func) { options.func(); }
- } else if (Q('d2cmdsource').value == 1) {
- // From file
- var reader = new FileReader();
- reader.onload = function (e) { cmd.cmds = e.target.result; meshserver.send(cmd); if (options.func) { options.func(); } }
- reader.readAsText(Q('d2runfileex').files[0]);
- } else if (Q('d2cmdsource').value == 2) {
- // From server file
- var files = d3getFileSel();
- if (files.length != 1) return;
- cmd.cmdpath = d3filetreelocation.join('/') + '/' + files[0];
- meshserver.send(cmd);
- if (options.func) { options.func(); }
- }
- }
- // Look to see if we need to update the device timeline
- function updateDeviceTimeline() {
- if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null) || (currentNode.mtype == 3)) return;
- if ((powerTimelineNode == powerTimelineReq) && (currentNode._id == powerTimelineNode) && (powerTimelineUpdate < Date.now())) { powerTimelineUpdate = null; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
- }
- // Draw device power bars. The bars are 766px wide.
- function drawDeviceTimeline() {
- if (currentNode.mtype == 3 || hidePowerTimeline === 'true') { QH('p10html2', '<br />'); return; }
- var timeline = null, now = Date.now();
- if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
- // Calculate when the timeline starts
- var d = new Date();
- d.setHours(0, 0, 0, 0);
- d = new Date(d.getTime() - (1000 * 60 * 60 * 24 * 6));
- var timelineStart = d.getTime();
- // De-compact the timeline
- var timeline2 = [];
- if (timeline != null && timeline.length > 1) {
- timeline2.push([0, timeline[1], timeline[0]]); // Start, End, Power
- var ct = timeline[1];
- for (var i = 2; i < timeline.length; i += 2) {
- var power = timeline[i], dt = now;
- if (timeline.length > (i + 1)) { dt = timeline[i + 1]; }
- timeline2.push([ct, ct + dt, power]); // Start, End, Power
- ct = ct + dt;
- }
- }
- // Draw the timeline
- var x = '', count = 1, date = new Date();
- var totalWidth = Q('masthead').offsetWidth - (90 + 9 + 9 + 14); // Compute the total width of the power bar
- date.setHours(0, 0, 0, 0);
- for (var i = 0; i < 7; i++) {
- var datavalue = '', start = date.getTime(), end = start + (1000 * 60 * 60 * 24);
- for (var j in timeline2) {
- var block = timeline2[j];
- if (isTimeBlockInside(start, end, block[0], block[1]) == true) {
- var ts = Math.max(start, block[0]);
- var te = Math.min(Math.min(end, block[1]), now);
- var width = Math.round(((te - ts) * totalWidth) / 86400000);
- if (width > 0) { datavalue += '<div style=display:table-cell;width:' + width + 'px;background-color:' + powerColor(block[2]) + ';height:16px></div>'; }
- }
- }
- x += '<tr style=' + (((count % 2) == 0) ? 'background-color:#DDD' : '') + '><td><div> ' + printDate(date) + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>';
- ++count;
- date = new Date(date.getFullYear(), date.getMonth(), date.getDate() - 1);
- }
- QH('p10html2', '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse;width:calc(100% - 18px);margin:9px" border=0 cellpadding=2 cellspacing=0><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:center;width:90px>' + "Day" + '</th><th scope=col style=text-align:center>' + "Power State" + '</th></tr>' + x + '</tbody></table>');
- }
- // Return a color for the given power state
- function powerColor(x) { if (x < powerColorTable.length) { return powerColorTable[x]; } return 'yellow'; }
- // Return true if the time block is visible within the start/end period
- function isTimeBlockInside(start, end, blockStart, blockEnd) {
- if ((blockStart < start) && (blockEnd > end)) return true; // Block is wider than timespan
- if ((blockStart > start) && (blockStart < end)) return true;
- if ((blockEnd > start) && (blockEnd < end)) return true;
- return false;
- }
- function addDeviceAttribute(name, value) {
- return '<tr><td style=width:100px;color:gray>' + name + '</td><td style=overflow:hidden>' + value + '</td></tr>';
- }
- function editDeviceAmtSettings(nodeid, func) {
- if (xxdialogMode) return;
- var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node);
- if ((meshrights & 4) == 0) return;
- x += addHtmlValue("Username", '<input id=dp10username style=width:170px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
- x += addHtmlValue("Password", '<input id=dp10password type=password style=width:170px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
- // Only display the TLS setting if the Intel AMT manager is not running on the server. With the manager TLS is auto-detected.
- if ((features2 & 1) == 0) { x += addHtmlValue("Security", '<select id=dp10tls style=width:176px><option value=0>' + "No TLS security" + '</option><option value=1>' + "TLS security required" + '</option></select>'); }
- if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
- setDialogMode(2, "Edit Intel® AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func });
- if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; }
- if ((features2 & 1) == 0) { Q('dp10tls').value = node.intelamt.tls; }
- validateDeviceAmtSettings();
- }
- function validateDeviceAmtSettings() {
- QE('idx_dlgOkButton', passwordcheck(Q('dp10password').value));
- }
- function editDeviceAmtSettingsEx(button, tag) {
- if (button == 2) {
- // Delete button pressed, remove credentials
- meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: '', pass: '' } });
- } else {
- // Change Intel AMT credentials
- var amtuser = Q('dp10username').value;
- if (amtuser == '') amtuser = 'admin';
- var amtpass = Q('dp10password').value;
- if (amtpass == '') amtuser = '';
- var x = { action: 'changedevice', nodeid: tag.node._id, intelamt: { user: amtuser, pass: amtpass } };
- if ((features2 & 1) == 0) { x.intelamt.tls = parseInt(Q('dp10tls').value); }
- meshserver.send(x);
- if (tag.func) { setTimeout(tag.func, 1000); }
- }
- }
- function p10showDeleteNodeDialog(nodeid) {
- if (xxdialogMode) return;
- setDialogMode(2, "Delete Node", 3, p10showDeleteNodeDialogEx, format("Delete {0}?", EscapeHtml(currentNode.name)) + '<br /><br /><label><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />' + "Confirm" + '</label>', nodeid);
- p10validateDeleteNodeDialog();
- }
- function p10validateDeleteNodeDialog() {
- QE('idx_dlgOkButton', Q('p10check').checked);
- }
- function p10showDeleteNodeDialogEx(buttons, nodeid) {
- meshserver.send({ action: 'removedevices', nodeids: [nodeid] });
- }
- function p10WebRouter(nodeid, protocol, port, addr) {
- var relayid = null;
- var node = getNodeFromId(nodeid);
- if (node.mtype == 3) { // Setup device relay if needed
- var mesh = meshes[node.meshid];
- if (mesh && mesh.relayid) { relayid = mesh.relayid; addr = node.host; }
- }
- var servername = serverinfo.name;
- if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
- if (webRelayDns != '') { servername = webRelayDns; }
- var url = 'https://' + servername + ':' + webRelayPort + '/control-redirect.ashx?n=' + nodeid + '&p=' + port + '&appid=' + protocol + '&c=' + authRelayCookie; // Protocol: 1 = HTTP, 2 = HTTPS
- if (addr != null) { url += '&addr=' + addr; }
- if (relayid != null) { url += '&relayid=' + relayid; }
- safeNewWindow(url, 'WebRelay');
- return false;
- }
- function p10rfb(nodeid, port) {
- var node = getNodeFromId(nodeid), addr = null;
- var mesh = meshes[node.meshid];
- if (port == null) { if (node.rfbport != null) { port = node.rfbport; } else { port = 5900; } }
- if (node.mtype == 3) { if (mesh && mesh.relayid) { nodeid = mesh.relayid; addr = node.host; } } // Setup device relay if needed
- meshserver.send({ action: 'getcookie', nodeid: nodeid, tcpport: port, tcpaddr: addr, tag: 'novnc', name: mesh ? mesh.name : null });
- }
- function p10mstsc(nodeid, port) {
- var node = getNodeFromId(nodeid);
- var mesh = meshes[node.meshid];
- if (port == null) { if (node.rdpport != null) { port = node.rdpport; } else { port = 3389; } } // Adjust RDP port if needed
- meshserver.send({ action: 'getcookie', nodeid: nodeid, tcpport: port, tag: 'mstsc', name: mesh ? mesh.name : null });
- }
- function p10ssh(nodeid, port) {
- var node = getNodeFromId(nodeid);
- var mesh = meshes[node.meshid];
- if (port == null) { if (node.sshport != null) { port = node.sshport; } else { port = 22; } }
- meshserver.send({ action: 'getcookie', nodeid: nodeid, tcpport: port, tag: 'ssh', name: mesh ? mesh.name : null });
- }
- function p10showiconselector() {
- if (xxdialogMode) return;
- var rights = GetNodeRights(currentNode);
- if ((rights & 4) == 0) return;
- var x = '<table align=center><td style=text-align:center>';
- x += '<div style=display:inline-block class=i1 onclick=p10setIcon(1)></div>';
- x += '<div style=display:inline-block class=i2 onclick=p10setIcon(2)></div>';
- x += '<div style=display:inline-block class=i3 onclick=p10setIcon(3)></div>';
- x += '<div style=display:inline-block class=i4 onclick=p10setIcon(4)></div><br />';
- x += '<div style=display:inline-block class=i5 onclick=p10setIcon(5)></div>';
- x += '<div style=display:inline-block class=i6 onclick=p10setIcon(6)></div>';
- x += '<div style=display:inline-block class=i7 onclick=p10setIcon(7)></div>';
- x += '<div style=display:inline-block class=i8 onclick=p10setIcon(8)></div></table>';
- setDialogMode(2, "Icon Selection", 0, null, x);
- QV('id_dialogclose', true);
- }
- function p10setIcon(icon) {
- setDialogMode(0);
- meshserver.send({ action: 'changedevice', nodeid: currentNode._id, icon: icon });
- }
- function showClearSshDialog() { setDialogMode(2, "Edit Device", 3, showClearSshDialogEx, "Clear SSH credentials?"); }
- function showClearSshDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, ssh: 0 }); }
- function showClearRdpDialog() { setDialogMode(2, "Edit Device", 3, showClearRdpDialogEx, "Clear RDP credentials?"); }
- function showClearRdpDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, rdp: 0 }); }
- var showEditNodeValueDialog_modes = ["Device Name", "Hostname", "Description", "Tags"];
- var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags'];
- var showEditNodeValueDialog_modes3 = ['', '', '', "Group1, Group2, Group3"];
- function showEditNodeValueDialog(mode) {
- if (xxdialogMode) return;
- var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:170px maxlength=64 placeholder="' + showEditNodeValueDialog_modes3[mode] + '" onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />');
- if (mode == 3) {
- // Get a list of all possible device tags
- var allTags = [], y = '';
- for (var i in nodes) { if (nodes[i].tags) { for (var j in nodes[i].tags) { if (allTags.indexOf(nodes[i].tags[j]) == -1) { allTags.push(nodes[i].tags[j]); } } } }
- if (allTags.length > 0) {
- allTags.sort();
- for (var i in allTags) { y += '<span style=padding:4px;background-color:#BBB;border-radius:3px;cursor:pointer onclick=showEditNodeValueDialogAddTag("' + encodeURIComponentEx(allTags[i]) + '")>' + EscapeHtml(allTags[i]) + '</span> '; }
- x += '<div style=margin-top:8px;width:280px;line-height:26px;max-height:160px;overflow-y:auto>' + y + '</div>';
- }
- }
- setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode);
- var v = currentNode[showEditNodeValueDialog_modes2[mode]];
- if (v == null) v = '';
- if (Array.isArray(v)) { v = v.join(', '); }
- Q('dp10devicevalue').value = v;
- p10editdevicevalueValidate();
- Q('dp10devicevalue').focus();
- }
- function showEditNodeValueDialogAddTag(t) {
- var tt = Q('dp10devicevalue').value.split(','), t2 = [];
- for (var i in tt) { t2.push(tt[i].trim()); }
- if (t2.indexOf(t) >= 0) return;
- Q('dp10devicevalue').value += ((Q('dp10devicevalue').value.length == 0) ? '' : ', ') + decodeURIComponent(t);
- setTimeout(function () { Q('dp10devicevalue').selectionStart = Q('dp10devicevalue').selectionEnd = 90000; }, 0);
- p10editdevicevalueValidate();
- }
- function showEditNodeValueDialogEx(button, mode) {
- var x = { action: 'changedevice', nodeid: currentNode._id };
- x[showEditNodeValueDialog_modes2[mode]] = Q('dp10devicevalue').value;
- meshserver.send(x);
- }
- function p10editdevicevalueValidate(mode, e) {
- var x = ((mode > 1) || (Q('dp10devicevalue').value.length > 0));
- QE('idx_dlgOkButton', x);
- if ((e != null) && (x == true) && (e.keyCode == 13)) { dialogclose(1); }
- }
- //
- // DESKTOP
- //
- var desktop;
- var desktopNode;
- var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 50, autolock: false, agentencoding: 4 };
- function setupDesktop() {
- // Setup the remote desktop
- if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); desktopNode = null; desktop = null; }
- // If the device desktop is already connected in multi-desktop, use that.
- if ((desktopNode != currentNode) || (desktop == null)) {
- // Device is not already connected, just setup a blank canvas
- //QH('DeskParent', '<canvas id=Desk width=640 height=200 style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
- desktopNode = currentNode;
- // Setup the mouse wheel
- Q('Desk').addEventListener('DOMMouseScroll', function (e) { return dmousewheel(e); });
- Q('Desk').addEventListener('mousewheel', function (e) { return dmousewheel(e); });
- }
- desktopNode = currentNode;
- updateDesktopButtons();
- // On some browsers like IE, we can't save screen shots. Hide the scheenshot/capture buttons.
- if (!Q('Desk')['toBlob']) { QV('deskSaveBtn', false); }
- }
- // Show and enable the right buttons
- function updateDesktopButtons() {
- var mesh = meshes[currentNode.meshid];
- var deskState = 0;
- if (desktop != null) { deskState = desktop.State; }
- var meshrights = GetNodeRights(currentNode);
- // Show the right buttons
- QV('disconnectbutton1', (deskState != 0));
- QE('deskFullScreen', (deskState != 0));
- QV('connectbutton1', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (currentNode.agent != null) && (currentNode.agent.caps & 1));
- QV('connectbutton1h',
- (deskState == 0) &&
- (meshrights & 8) &&
- (
- ((currentNode.intelamt != null) &&
- (currentNode.intelamt.state == 2) &&
- (currentNode.intelamt.ver != null) &&
- ((currentNode.intelamt.sku == null) ||
- ((typeof currentNode.intelamt.sku == 'number') &&
- ((currentNode.intelamt.sku & 8) != 0))))
- )
- );
- // Show the right settings
- QV('d7amtkvm', (currentNode.intelamt != null && ((typeof currentNode.intelamt.sku != 'number') || ((currentNode.intelamt.sku & 16) == 0)) && ((currentNode.intelamt.ver != null) || (currentNode.agent == null))) && ((deskState == 0) || (desktop.contype == 2)));
- QV('d7meshkvm', ((currentNode.agent != null) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1))));
- // Enable buttons
- var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop
- QE('connectbutton1', online);
- var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
- QE('connectbutton1h', hwonline);
- //QE('deskSaveBtn', deskState == 3);
- //QV('DeskCAD', meshrights & 8);
- //QE('DeskCAD', deskState == 3);
- //QV('DeskWD', (currentNode.agent) && (currentNode.agent.id < 5));
- //QE('DeskWD', deskState == 3);
- //QV('deskkeys', (currentNode.agent) && (currentNode.agent.id < 5));
- //QE('deskkeys', deskState == 3);
- //QE('DeskToolsButton', online);
- QV('DeskToastButton', ((meshrights & 16384) != 0) && (currentNode.agent) && (currentNode.agent.id < 5) && (meshrights & 8));
- //QE('DeskToastButton', online);
- QV('deskActionsBtn', meshrights & 8);
- Q('DeskControl').checked = ((meshrights & 8) != 0);
- if (online == false) QV('DeskTools', false);
- }
- // Used to translate incoming agent console messages
- var agentConsoleMessages = ['', "Waiting for user to grant access...", "Denied", "Failed to start remote terminal session, {0} ({1})", "Timeout", "Received invalid network data"];
- function formatAgentConsoleMessage(msg, msgid, msgargs) {
- var r;
- if (msgargs == null) { msgargs = []; }
- while (msgargs.length < 3) { msgargs.push(''); } // We need to call the format function in a way that works with older browsers and minifier, can't use apply() or ...
- if (msgid && (msgid < agentConsoleMessages.length)) { r = EscapeHtml(format(agentConsoleMessages[msgid], (msgargs[0]), (msgargs[1]), (msgargs[2]))); } else { r = EscapeHtml(msg); }
- return r.split('\n').join('<br />') + '<br /><br />';
- }
- function connectDesktop(e, contype, tsid, consent) {
- setSessionActivity();
- QV('p11DeskSessionSelector', false);
- p11clearConsoleMsg();
- if (desktop == null) {
- desktopNode = currentNode;
- if (contype == 2) {
- // Setup the Intel AMT remote desktop
- if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; }
- desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'), authCookie);
- desktop.debugmode = debugmode;
- desktop.onStateChanged = onDesktopStateChange;
- desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2;
- desktop.m.useZRLE = (desktopsettings.encoding < 3);
- desktop.m.showmouse = true;
- desktop.m.onScreenSizeChange = function (o, x, y) { if (fullscreen) { QS('deskarea3').width = (x * fullscreenzoom) + 'px'; QS('deskarea3').height = (y * fullscreenzoom) + 'px'; } deskAdjust(); }
- // Use TLS if TLS is set
- if (desktopNode.conn==4 && desktopNode.intelamt!=null && desktopNode.intelamt.tls==1) {
- desktop.Start(desktopNode._id, 16995, '*', '*', 1);
- } else {
- desktop.Start(desktopNode._id, 16994, '*', '*', 0);
- }
- desktop.contype = 2;
- } else if ((contype == null) || (contype == 1) || ((contype == 3) && (currentNode.agent.id > 4))) {
- // Setup the Mesh Agent remote desktop
- desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
- desktop.debugmode = debugmode;
- desktop.m.debugmode = debugmode;
- desktop.attemptWebRTC = attemptWebRTC;
- desktop.webrtcconfig = webrtcconfiguration;
- desktop.options = {};
- if (tsid != null) { desktop.options.tsid = tsid; }
- if (consent != null) { desktop.options.consent = consent; }
- if (desktopsettings.autolock == true) { desktop.options.autolock = true; }
- desktop.onStateChanged = onDesktopStateChange;
- if ((features2 & 0x2000) != 0) desktop.m.stopInput = true;
- desktop.onConsoleMessageChange = function () {
- if (desktop.consoleMessage) {
- Q('p11DeskConsoleMsg').innerHTML += formatAgentConsoleMessage(desktop.consoleMessage, desktop.consoleMessageId, desktop.consoleMessageArgs);
- QV('p11DeskConsoleMsg', true);
- if (p11DeskConsoleMsgTimer != null) { clearTimeout(p11DeskConsoleMsgTimer); }
- if (desktop.consoleMessageTimeout) { p11DeskConsoleMsgTimer = setTimeout(p11clearConsoleMsg, desktop.consoleMessageTimeout * 1000); }
- } else {
- p11clearConsoleMsg();
- }
- }
- desktop.m.ImageType = desktopsettings.agentencoding; // Send 4 if WebP is supported, otherwise send 1 for JPEG.
- desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best.
- desktop.m.ScalingLevel = desktopsettings.scaling;
- desktop.m.FrameRateTimer = desktopsettings.framerate;
- desktop.m.onDisplayinfo = deskDisplayInfo;
- desktop.m.onScreenSizeChange = function (o, x, y) { if (fullscreen) { QS('deskarea3').width = (x * fullscreenzoom) + 'px'; QS('deskarea3').height = (y * fullscreenzoom) + 'px'; } deskAdjust(); }
- desktop.Start(desktopNode._id);
- desktop.contype = 1;
- } else if (contype == 3) {
- // Ask for user sessions
- meshserver.send({ action: 'msg', type: 'userSessions', nodeid: currentNode._id, tag: consent });
- }
- } else {
- // Disconnect and clean up the remote desktop
- desktop.Stop();
- desktopNode = desktop = null;
- }
- }
- function p11clearConsoleMsg() { QH('p11DeskConsoleMsg', ''); QV('p11DeskConsoleMsg', false); if (p11DeskConsoleMsgTimer) { clearTimeout(p11DeskConsoleMsgTimer); p11DeskConsoleMsgTimer = null; } }
- function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
- function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
- function p12setConsoleMsg(msg, timeout) {
- if (msg) {
- Q('p12TermConsoleMsg').innerHTML += msg;
- QV('p12TermConsoleMsg', true);
- if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); }
- if (timeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, timeout); }
- } else {
- p12clearConsoleMsg();
- }
- }
- function p13setConsoleMsg(msg, timeout) {
- if (msg) {
- Q('p13FilesConsoleMsg').innerHTML += msg;
- QV('p13FilesConsoleMsg', true);
- if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); }
- if (timeout) { p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, timeout); }
- } else {
- p13clearConsoleMsg();
- }
- }
- function onDesktopStateChange(xdesktop, state) {
- var xstate = state;
- if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
- var str = StatusStrs[xstate];
- if ((desktop != null) && (desktop.webRtcActive == true)) { str += ", WebRTC"; }
- //if (desktop.m.stopInput == true) { str += ', Loopback'; }
- QH('deskstatus', str);
- switch (state) {
- case 0:
- // Disconnect and clean up the remote desktop
- desktop.Stop();
- desktopNode = desktop = null;
- QV('DeskScreens', false);
- if (fullscreen == true) { deskToggleFull(); }
- break;
- case 2:
- break;
- default:
- //console.log('Unknown onDesktopStateChange state', state);
- break;
- }
- updateDesktopButtons();
- deskAdjust();
- setTimeout(deskAdjust, 50);
- }
- function showDesktopSettings() {
- if (xxdialogMode) return;
- applyDesktopSettings();
- updateDesktopButtons();
- setDialogMode(7, "Remote Desktop Settings", 3, showDesktopSettingsChanged);
- }
- function showDesktopSettingsChanged() {
- desktopsettings.encoding = d7desktopmode.value;
- desktopsettings.quality = d7bitmapquality.value;
- desktopsettings.scaling = d7bitmapscaling.value;
- desktopsettings.framerate = d7framelimiter.value;
- desktopsettings.autolock = d7deskAutoLock.checked;
- desktopsettings.agentencoding = d7encoding.value;
- localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
- applyDesktopSettings();
- if (desktop) {
- if (desktop.contype == 1) {
- if (desktop.State != 0) { desktop.m.SendCompressionLevel(desktopsettings.agentencoding, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate); }
- desktop.sendCtrlMsg('{"ctrlChannel":"102938","type":"autolock","value":' + desktopsettings.autolock + '}');
- desktop.m.SendRefresh();
- }
- if (desktop.contype == 2) {
- if (desktop.State != 0) { desktop.Stop(); setTimeout(function () { connectDesktop(null, 2); }, 50); }
- }
- }
- }
- function applyDesktopSettings() {
- var r = '', ops = (features & 512) ? [100, 90, 70, 50, 40, 30, 20, 10, 5, 1] : [50, 40, 30, 20, 10, 5, 1];
- for (var i in ops) { r += '<option value=' + ops[i] + '>' + ops[i] + '%</option>'; }
- QH('d7bitmapquality', r);
- d7desktopmode.value = desktopsettings.encoding;
- d7bitmapquality.value = 40; // Default value
- if (desktopsettings.agentencoding) { d7encoding.value = desktopsettings.agentencoding; } else { desktopsettings.agentencoding = 4; }
- if (ops.indexOf(parseInt(desktopsettings.quality)) >= 0) { d7bitmapquality.value = desktopsettings.quality; }
- d7bitmapscaling.value = desktopsettings.scaling;
- if (desktopsettings.framerate) { d7framelimiter.value = desktopsettings.framerate; }
- if (desktopsettings.autolock != null) { d7deskAutoLock.checked = desktopsettings.autolock; }
- }
- var keyboardShown = false;
- var keyboardShownTimer = null;
- var fullScreenMode = false;
- function toggleKeyboard() {
- if (xxdialogMode) return;
- if (keyboardShownTimer != null) { clearTimeout(keyboardShownTimer); }
- if (keyboardShown) { Q('softKeyboard').blur(); keyboardShown = false; } else { Q('softKeyboard').focus(); keyboardShown = true; }
- QV('deskkeybutton2a', fullscreen && !keyboardShown);
- QV('deskkeybutton2b', fullscreen && keyboardShown);
- }
- function keyboardFocusChange() {
- keyboardShownTimer = setTimeout(function () {
- keyboardShownTimer = null;
- keyboardShown = (Q('softKeyboard') == document.activeElement);
- QV('deskkeybutton2a', fullscreen && !keyboardShown);
- QV('deskkeybutton2b', fullscreen && keyboardShown);
- }, 10);
- }
- function exitButton() {
- if (xxdialogMode) return;
- QV('deskButtonMenu', false);
- QV('termButtonMenu', false);
- deskToggleFull();
- }
- function deskMenuButton(x) {
- toggleMenu(true);
- deskSendKeys(x);
- }
- //
- // Desktop Shortcut Keys
- //
- function updateDeskShortcutKeys() {
- var x = '<div class="menuButton" onclick="deskMenuButton(-1)">' + "Customize" + '</div>';
- for (var i in deskKeyboardShortcuts) { x += '<div class="menuButton" onclick="deskMenuButton(' + deskKeyboardShortcuts[i] + ')">' + keyShortcutTotext(deskKeyboardShortcuts[i]) + '</div>'; }
- QH('deskButtonMenu', x);
- }
- var keyStrings = { 8: "BackSpace", 9: "Tab", 13: "Enter", 27: "Escape", 32: "Space", 44: "Print Screen", 45: "Insert", 46: "Del", 36: "Home", 35: "End", 32: "Espace", 33: "Page Up", 34: "Page Down", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 0: "None" }
- function keyShortcutTotext(n) {
- var x = [];
- if (n & 0x010000) { x.push("Shift"); }
- if (n & 0x020000) { x.push("Alt"); }
- if (n & 0x080000) { x.push("Ctrl"); }
- if (n & 0x100000) { x.push("Win"); }
- n = (n & 0xFFFF);
- if ((n >= 112) && (n <= 123)) { x.push('F' + (n - 111)); } // Fx keys
- else if ((n != 0) && (keyStrings[n])) { x.push(keyStrings[n]); }
- else { if (n != 0) { x.push(String.fromCharCode(n)); } }
- return x.join(' + ');
- }
- // Customize keyboard shortcuts
- function deskCustomizeKeys() {
- if (xxdialogMode) return;
- var x = '<div id=d2shortcuts style="width:100%;height:180px;padding:4px;overflow-y:auto;border:1px solid gray"></div><div style=width:100%;padding:5px>';
- x += '<label><input id=d1kshift type=checkbox /> ' + "Shift" + '</label><label> <input id=d1kalt type=checkbox /> ' + "Alt" + '</label><label> <input id=d1kctrl type=checkbox /> ' + "Ctrl" + '</label> <input id=d1kwin type=checkbox /> ' + "Win" + '</label>';
- x += ' <select id=d2keySelect>';
- for (var i in keyStrings) { x += '<option value=' + i + '>' + keyStrings[i] + '</option>'; }
- for (var i = 1; i <= 12; i++) { x += '<option value=' + (i + 111) + '>F' + i + '</option>'; }
- for (var i = 0; i < 10; i++) { x += '<option value=' + (i + 48) + '>' + i + '</option>'; }
- for (var i = 0; i < 26; i++) { x += '<option value=' + (i + 65) + '>' + String.fromCharCode(i + 65) + '</option>'; }
- x += '</select> <input type=button value=' + "Add" + ' onclick=addDeskCustomizeKey() /></div>';
- QH('p10dialog2', x);
- xxdialogMode = 2;
- QV('p10dialog', true);
- deskUpdateShortcutList();
- }
- function deskCustomizeKeysEx() {
- QV('p10dialog', false);
- xxdialogMode = 0;
- putstore('deskKeyShortcuts', deskKeyboardShortcuts.join(','));
- updateDeskShortcutKeys();
- }
- function restoreDeskCustomizeKey() {
- deskKeyboardShortcuts = [];
- putstore('deskKeyShortcuts', null);
- var deskKeyboardShortcutsStr = getstore('deskKeyShortcuts', '0x0A002E,0x100000,0x100028,0x100026,0x10004C,0x10004D,0x11004D,0x100052,0x020073,0x080057,0x020009,0x100025,0x100027').split(',');
- for (var i in deskKeyboardShortcutsStr) { if (deskKeyboardShortcutsStr[i] != "") { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); } }
- updateDeskShortcutKeys();
- deskUpdateShortcutList();
- }
- function deskUpdateShortcutList() {
- var x = '';
- for (var i in deskKeyboardShortcuts) {
- var kt = keyShortcutTotext(deskKeyboardShortcuts[i]), orderButtons = '';
- if (i != (deskKeyboardShortcuts.length - 1)) { orderButtons += '<img width=8 height=8 style=float:right;cursor:pointer;padding:3px src="images/c2.png" onclick=deskCustomizeKeyDown(' + deskKeyboardShortcuts[i] + ')>'; }
- if (i != 0) { orderButtons += '<img width=8 height=8 style=float:right;cursor:pointer;padding:3px src="images/c3.png" onclick=deskCustomizeKeyUp(' + deskKeyboardShortcuts[i] + ')>'; }
- x += '<div style="width:100%;background-color:#AAA;border-radius:4px;margin-bottom:4px;padding:4px;text-align:left;box-sizing:border-box" value=' + deskKeyboardShortcuts[i] + '>' + kt + '<img width=10 height=10 style=float:right;cursor:pointer;padding:2px;margin-left:8px src="images/trash.png" onclick=removeDeskCustomizeKey(' + deskKeyboardShortcuts[i] + ')>' + orderButtons + '</div>';
- }
- if (x == '') { x = '<i>' + "No keyboard shortcuts defined" + '</i>'; }
- QH('d2shortcuts', x);
- }
- function deskCustomizeKeyDown(k) {
- var i = deskKeyboardShortcuts.indexOf(k), x = deskKeyboardShortcuts[i + 1];
- deskKeyboardShortcuts[i + 1] = deskKeyboardShortcuts[i];
- deskKeyboardShortcuts[i] = x;
- deskUpdateShortcutList();
- }
- function deskCustomizeKeyUp(k) {
- var i = deskKeyboardShortcuts.indexOf(k), x = deskKeyboardShortcuts[i];
- deskKeyboardShortcuts[i] = deskKeyboardShortcuts[i - 1];
- deskKeyboardShortcuts[i - 1] = x;
- deskUpdateShortcutList();
- }
- function removeDeskCustomizeKey(k) {
- var na = [];
- for (var i in deskKeyboardShortcuts) { if (deskKeyboardShortcuts[i] != k) { na.push(deskKeyboardShortcuts[i]); } }
- deskKeyboardShortcuts = na;
- deskUpdateShortcutList();
- }
- function addDeskCustomizeKey() {
- var k = parseInt(Q('d2keySelect').value);
- if (Q('d1kshift').checked) { k |= 0x010000; }
- if (Q('d1kalt').checked) { k |= 0x020000; }
- if (Q('d1kctrl').checked) { k |= 0x080000; }
- if (Q('d1kwin').checked) { k |= 0x100000; }
- if ((k > 0) && (deskKeyboardShortcuts.indexOf(k) == -1)) { deskKeyboardShortcuts.push(k); deskUpdateShortcutList(); }
- }
- // Remote desktop special key combos for Windows
- function deskSendKeys(ks) {
- if (xxdialogMode || desktop == null || desktop.State != 3) return;
- // Construct the key command
- if (ks == -1) { deskCustomizeKeys(); return; } // Customize
- if (ks == 0x0A002E) { desktop.m.sendcad(); return; } // CTRL-ALT-DEL
- //if ((desktop.contype == 1) && (ks == 0x10004C)) { desktop.sendCtrlMsg('{"action":"lock"}'); return; } // Lock desktop, WIN + L
- var flags = (ks & 0xFF0000) >> 16, key = (ks & 0xFFFF), keyArray = [], keyArray2 = [];
- var amtTranslate = {
- 8: 0xff08, // BackSpace
- 9: 0xff09, // Tab
- 13: 0xff0d, // Return or Enter
- 27: 0xff1b, // Escape
- 45: 0xff63, // Insert
- 46: 0xffff, // Delete
- 36: 0xff50, // Home
- 35: 0xff57, // End
- 33: 0xff55, // Page Up
- 34: 0xff56, // Page Down
- 37: 0xff51, // Left arrow
- 38: 0xff52, // Up arrow
- 39: 0xff53, // Right arrow
- 40: 0xff54, // Down arrow
- 112: 0xffbe, // F1
- 113: 0xffbf, // F2
- 114: 0xffc0, // F3
- 115: 0xffc1, // F4
- 116: 0xffc2, // F5
- 117: 0xffc3, // F6
- 118: 0xffc4, // F7
- 119: 0xffc5, // F8
- 120: 0xffc6, // F9
- 121: 0xffc7, // F10
- 122: 0xffc8, // F11
- 123: 0xffc9 // F12
- }
- // 0x010000 = Shift
- // 0x020000 = Left-Alt
- // 0x080000 = Ctrl
- // 0x100000 = Window
- if (desktop.contype == 2) {
- // Intel AMT
- if (flags & 1) { keyArray.push([0xffe1, 1]); keyArray2.push([0xffe1, 0]); } // Shift
- if (flags & 2) { keyArray.push([0xffe9, 1]); keyArray2.push([0xffe9, 0]); } // Left-alt
- if (flags & 8) { keyArray.push([0xffe3, 1]); keyArray2.push([0xffe3, 0]); } // Ctrl
- if (flags & 16) { keyArray.push([0xffe7, 1]); keyArray2.push([0xffe7, 0]); } // Windows key
- if (amtTranslate[key]) { key = amtTranslate[key]; }
- if ((key >= 65) && (key <= 90)) { key += 32; }
- if (key != 0) { keyArray.push([key, 1]); keyArray2.push([key, 0]); }
- keyArray2.reverse();
- for (var i = 0; i < keyArray2.length; i++) { keyArray.push(keyArray2[i]); }
- desktop.m.sendkey(keyArray);
- } else {
- // Agent desktop
- if (flags & 1) { keyArray.push([desktop.m.KeyAction.DOWN, 16]); keyArray2.push([desktop.m.KeyAction.UP, 16]); } // Shift
- if (flags & 2) { keyArray.push([desktop.m.KeyAction.EXDOWN, 18]); keyArray2.push([desktop.m.KeyAction.EXUP, 18]); } // Left-alt
- if (flags & 8) { keyArray.push([desktop.m.KeyAction.EXDOWN, 17]); keyArray2.push([desktop.m.KeyAction.EXUP, 17]); } // Ctrl
- if (flags & 16) { keyArray.push([desktop.m.KeyAction.EXDOWN, 0x5B]); keyArray2.push([desktop.m.KeyAction.EXUP, 0x5B]); } // Windows key
- if (key != 0) { keyArray.push([desktop.m.KeyAction.DOWN, key]); keyArray2.push([desktop.m.KeyAction.UP, key]); }
- keyArray2.reverse();
- for (var i = 0; i < keyArray2.length; i++) { keyArray.push(keyArray2[i]); }
- desktop.m.SendKeyMsgKC(keyArray);
- }
- }
- function toggleMenu(x) {
- if (xxdialogMode) return;
- QV('deskButtonMenu', fullscreen && !x && (currentDevicePanel == 1));
- QV('termButtonMenu', fullscreen && !x && (currentDevicePanel == 5));
- QV('deskkeybutton3a', fullscreen && x);
- QV('deskkeybutton3b', fullscreen && !x);
- }
- function deskChangeMouseButton(x) {
- if (xxdialogMode) return;
- if (desktop == null) return;
- desktop.m.SwapMouse = !desktop.m.SwapMouse;
- QV('deskkeybutton4a', fullscreen && (!desktop.m.SwapMouse));
- QV('deskkeybutton4b', fullscreen && (desktop.m.SwapMouse));
- }
- function deskChangeFullscreenZoom() {
- if (xxdialogMode) return;
- if (currentDevicePanel == 1) {
- if (desktop == null) return;
- if (fullscreenzoom == 1) { fullscreenzoom = 0.5; } else { fullscreenzoom = 1; }
- QV('deskkeybutton5a', fullscreen && (fullscreenzoom == 1));
- QV('deskkeybutton5b', fullscreen && (fullscreenzoom != 1));
- QS('deskarea3').width = (desktop.m.ScreenWidth * fullscreenzoom) + 'px';
- QS('deskarea3').height = (desktop.m.ScreenHeight * fullscreenzoom) + 'px';
- deskAdjust();
- }
- if (currentDevicePanel == 5) {
- if (terminal == null) return;
- xterm.setOption('fontSize', (xterm.getOption('fontSize') == 15) ? 10 : 15)
- }
- }
- var fullscreen = false;
- var fullscreenzoom = 1;
- function deskToggleFull() {
- fullscreen = !fullscreen;
- QV('mastheadx', !fullscreen);
- QV('masthead', !fullscreen);
- QV('topbar', !fullscreen);
- QV('p11deviceNameHeader', !fullscreen);
- QV('footer', !fullscreen);
- QV('column_l_bottomgap', !fullscreen);
- QV('idx_deskFullBtn2', fullscreen);
- QV('deskFullBtn', !fullscreen);
- QV('p10deskTopTable', !fullscreen);
- QV('deskarea1', !fullscreen);
- QV('deskarea4', !fullscreen);
- QV('termarea1', !fullscreen);
- QV('termarea4', !fullscreen);
- var rights = GetNodeRights(currentNode);
- var inputAllowed = ((features2 & 0x2000) == 0) && (currentNode.agent.id != 14) && ((rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0)));
- // Show full screen buttons if needed
- QV('deskkeybutton1', fullscreen);
- if (currentDevicePanel == 1) { // Desktop panel is being shown (1 = Desktop, 5 = Terminal)
- // Move shortcut key to desktop position
- QS('deskkeybutton2a').top = QS('deskkeybutton2b').top = '210px';
- // Move the zoom button to normal or top position
- QS('deskkeybutton5a').top = QS('deskkeybutton5b').top = (inputAllowed) ? '160px' : '60px'; // Zoom
- QV('deskkeybutton2a', fullscreen && inputAllowed);
- QV('deskkeybutton2b', false);
- QV('deskkeybutton3a', fullscreen && inputAllowed);
- QV('deskkeybutton3b', false);
- QV('deskkeybutton4a', fullscreen && inputAllowed && (!desktop.m.SwapMouse));
- QV('deskkeybutton4b', fullscreen && inputAllowed && (desktop.m.SwapMouse));
- QV('deskkeybutton5a', fullscreen && (fullscreenzoom == 1));
- QV('deskkeybutton5b', fullscreen && (fullscreenzoom != 1));
- }
- if (currentDevicePanel == 5) {
- // Move right buttons to terminal position
- //QS('deskkeybutton3a').top = QS('deskkeybutton3b').top = '60px'; // Shortcuts
- //QS('deskkeybutton5a').top = QS('deskkeybutton5b').top = '110px'; // Zoom
- QS('deskkeybutton2a').top = QS('deskkeybutton2b').top = '110px'; // Keyboard
- QV('deskkeybutton2a', fullscreen);
- QV('deskkeybutton2b', false);
- QV('deskkeybutton3a', fullscreen);
- QV('deskkeybutton3b', false);
- QV('deskkeybutton4a', false);
- QV('deskkeybutton4b', false);
- QV('deskkeybutton5a', false);
- QV('deskkeybutton5a', false);
- //QV('deskkeybutton5a', xterm.getOption('fontSize') == 15);
- //QV('deskkeybutton5b', xterm.getOption('fontSize') != 15);
- }
- if (fullscreen) {
- QS('DeskParent').height = null;
- QS('page_content').top = '0px';
- QS('page_content').bottom = '0px';
- if (currentDevicePanel == 1) {
- QS('p10desktop').top = '0px';
- QS('p10desktop').overflow = 'scroll';
- QS('deskarea3').top = '0px';
- QS('deskarea3').width = (desktop.m.ScreenWidth * fullscreenzoom) + 'px';
- QS('deskarea3').height = (desktop.m.ScreenHeight * fullscreenzoom) + 'px';
- QS('deskarea3')['padding-right'] = '55px';
- }
- if (currentDevicePanel == 5) {
- QS('p10terminal').top = '0px';
- QS('p10terminal').overflow = 'scroll';
- QS('termarea3').top = '0px';
- QS('termarea3').bottom = null;
- QS('termarea3').right = null;
- QS('termarea3')['padding-right'] = '55px';
- QS('termarea3')['height'] = '100%';
- }
- QS('body')['background-color'] = '#000';
- QS('p10')['background-color'] = '#000';
- } else {
- QS('DeskParent').height = '100%';
- QS('page_content').top = '50px';
- QS('page_content').bottom = '32px';
- if (currentDevicePanel == 1) {
- QS('p10desktop').top = '55px';
- QS('p10desktop').overflow = 'hidden';
- QS('deskarea3').top = '32px';
- QS('deskarea3').left = null;
- QS('deskarea3').width = '100%';
- QS('deskarea3').height = 'calc(100% - 64px)';
- QS('deskarea3')['padding-right'] = '';
- QS('DeskParent')['margin-top'] = null;
- QS('DeskParent')['margin-left'] = null;
- }
- if (currentDevicePanel == 5) {
- //xterm.setOption('fontSize', 15)
- QS('p10terminal').top = '55px';
- QS('p10terminal').overflow = 'hidden';
- Q('p10terminal').scrollTop = 0;
- Q('p10terminal').scrollLeft = 0;
- QS('termarea3').top = '32px';
- QS('termarea3').bottom = '32px';
- //QS('termarea3').right = '0px';
- QS('termarea3')['padding-right'] = null;
- QS('termarea3')['height'] = 'calc(100% - 60px)';
- }
- QS('body')['background-color'] = nightMode ? '#000' : '#FFF';
- QS('p10')['background-color'] = null;
- }
- if (currentDevicePanel == 1) { deskAdjust(); }
- }
- function deskAdjust() {
- if (currentDevicePanel != 1) return; // If not on desktop tab, ignore this.
- if (fullscreen) {
- QS('Desk')['margin-top'] = null;
- QS('Desk')['margin-bottom'] = null;
- QS('Desk').width = '100%';
- QS('Desk').height = '100%';
- var parentH = Q('p10desktop').clientHeight, parentW = Q('p10desktop').clientWidth;
- var deskH = Q('deskarea3').clientHeight, deskW = Q('deskarea3').clientWidth - 55;
- if (parentH > deskH) { QS('deskarea3').top = ((parentH - deskH) / 2) + 'px'; } else { QS('deskarea3').top = null; }
- if (parentW > deskW) { QS('deskarea3').left = ((parentW - deskW) / 2) + 'px'; } else { QS('deskarea3').left = null; }
- } else {
- var parentH = Q('DeskParent').clientHeight, parentW = Q('DeskParent').clientWidth;
- var deskH = Q('Desk').height, deskW = Q('Desk').width;
- var webPageFullScreen = false;
- // Fixed aspect ratio
- if ((parentH / parentW) > (deskH / deskW)) {
- var hNew = ((deskH * parentW) / deskW) + 'px';
- QS('Desk').height = hNew;
- QS('Desk').width = '100%';
- } else {
- var wNew = ((deskW * parentH) / deskH) + 'px';
- QS('Desk').width = wNew;
- QS('Desk').height = '100%';
- }
- QS('DeskParent').overflow = 'hidden';
- // Adjust top/bottom margins
- var x = (Q('DeskParent').clientHeight - Q('Desk').clientHeight) / 2;
- QS('Desk')['margin-top'] = x + 'px';
- QS('Desk')['margin-bottom'] = x + 'px';
- }
- }
- function sendSpecialKeys() {
- if (xxdialogMode || desktop == null || desktop.State != 3) return;
- setDialogMode(3, "Special Keys", 3, deskSendKeys);
- }
- // Save the desktop image to file
- function deskSaveImage() {
- setSessionActivity();
- if (xxdialogMode || desktop == null || desktop.State != 3) return;
- var d = new Date(), n = 'Desktop-' + currentNode.name + '-' + d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + '-' + ('0' + d.getHours()).slice(-2) + '-' + ('0' + d.getMinutes()).slice(-2);
- Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.png'); });
- }
- function deskSelectScreens() {
- if (xxdialogMode || desktop == null || desktop.State != 3) return;
- var x = '', info = desktop.m.displays;
- for (var i in info) { x += '<option value=' + i + ' ' + ((desktop.m.selectedDisplay == i) ? ' selected' : '') + '>' + info[i] + '</option>'; }
- x = addHtmlValue4("Screen", '<select style=width:100% id=deskdisplays>' + x + '</select>');
- setDialogMode(2, "Screen Selection", 3, deskSelectScreensEx, x);
- }
- function deskSelectScreensEx() {
- if (desktop == null || desktop.State != 3) return;
- desktop.m.SetDisplay(parseInt(Q('deskdisplays').value));
- }
- function deskDisplayInfo(sender, info, selDisplay, selItem) {
- var displayCount = 0;
- for (var x in info) { displayCount++; }
- QV('DeskScreens', displayCount > 1);
- }
- function dmousedown(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousedown(e); } }
- function dmouseup(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mouseup(e); } }
- function dmousemove(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousemove(e); } }
- function dmousewheel(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null) && desktop.m.mousewheel) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
- function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); } }
- //
- // TERMINAL
- //
- var terminalNode;
- function setupTerminal() {
- // Setup the terminal
- if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); terminal = null; }
- terminalNode = currentNode;
- updateTerminalButtons();
- }
- // Show and enable the right buttons
- function updateTerminalButtons() {
- var mtype = (currentNode.agent == 1) ? 1 : 2;
- var termState = ((terminal != null) && (terminal.state != 0));
- QE('termFullScreen', (termState != 0));
- // If we are looking at a local non-windows device, enable terminal and files capability.
- if ((terminalNode.mtype == 3) && (terminalNode.agent != null) && (terminalNode.agent.id > 4) && (features2 & 0x00000200)) { terminalNode.agent.caps = 6; }
- // Show the right buttons
- QV('disconnectbutton2span', (termState == true));
- QV('connectbutton2span', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.mtype != 3));
- QV('connectbutton2sspan', (termState == false) && (terminalNode.agent != null) && (terminalNode.agent.caps & 2) && (terminalNode.agent.id != 3));
- // Enable buttons
- var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal
- QE('connectbutton2', online);
- QE('connectbutton2s', online);
- // Enable action button if mesh type is not "local devices"
- QV('termActionsBtn', terminalNode.mtype != 3);
- QE('ctrlcbutton', termState);
- QE('ctrlxbutton', termState);
- QE('escbutton', termState);
- if (((termState == true) && (terminal.contype != 3)) || (terminalNode.agent == null) || (terminalNode.agent.id == 3) || (terminalNode.agent.id == 4)) {
- QH('terminalCustomUpperRight', '');
- } else {
- QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport ? terminalNode.sshport : 22)) + '</a>');
- }
- }
- function cmsshportaction(action) {
- if (xxdialogMode) return;
- var x = "SSH remote connection port:" + '<br /><br /><input type=text placeholder="22" inputmode="numeric" pattern="[0-9]*" onkeypress="return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)" maxlength=5 id=d10sshport type=text>';
- setDialogMode(2, "SSH Connection", 3, function () {
- // Save the new SSH port to the server
- var sshport = ((Q('d10sshport').value.length > 0) ? parseInt(Q('d10sshport').value) : 22);
- meshserver.send({ action: 'changedevice', nodeid: currentNode._id, sshport: sshport });
- }, x, currentNode);
- Q('d10sshport').focus();
- if (currentNode.sshport != null) { Q('d10sshport').value = currentNode.sshport; }
- }
- // Called when the terminal state changes
- function onTerminalStateChange(xterminal, state) {
- var xstate = state;
- if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
- var str = StatusStrs[xstate];
- if (terminal.webRtcActive == true) { str += ", WebRTC"; }
- QH('termstatus', str);
- switch (state) {
- case 0:
- // Disconnected, clear the terminal
- xterm.dispose();
- xterm = null;
- if (terminal != null) { terminal.Stop(); terminal = null; }
- break;
- case 3:
- xterm.focus();
- break;
- default:
- //console.log('Unhandled onTerminalStateChange state', state);
- break;
- }
- updateTerminalButtons();
- }
- // Handles a tunnel to a remote shell
- function CreateRemoteTunnel(onTunnelUpdate, options) {
- var obj = { protocol: 1 };
- if ((options != null) && (typeof options.protocol == 'number')) { obj.protocol = options.protocol; }
- obj.onTunnelUpdate = onTunnelUpdate;
- obj.xxStateChange = function (state) { }
- obj.ProcessBinaryData = function (data) { obj.onTunnelUpdate(data); }
- obj.ProcessData = function (data) { obj.onTunnelUpdate(data); }
- obj.terminalEmulation = 1;
- obj.fxEmulation = 0;
- obj.lineFeed = '\r\n';
- return obj;
- }
- function tunnelUpdate(data) {
- if (xterm != null) {
- if (xterm.writeUtf8) {
- if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); }
- } else {
- if (typeof data == 'string') { xterm.write(data); } else { xterm.write(new Uint8Array(data)); }
- }
- }
- }
- //function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } }
- function sshTunnelAuthDialog(j, func) {
- var x = '';
- if (j.askkeypass) {
- x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=3 selected>' + "Stored Key" + '</option><option value=1>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
- } else {
- x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>');
- }
- x += '<div id=d2userauth style=display:none>';
- x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- x += '</div>';
- x += '<div id=d2passauth style=display:none>';
- x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- if ((features2 & 0x00400000) == 0) { x += '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'; }
- x += '</div><div id=d2keyauth style=display:none>';
- x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:150px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />' + '<div id=d2badkey style=font-size:x-small>' + "Key file must be in OpenSSH format." + '</div>');
- x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- if ((features2 & 0x00400000) == 0) {
- x += '<label><input id=dp2keep1 type=checkbox onchange=sshAuthUpdate(event)>' + "Remember user & key" + '</label><br/>';
- x += '<label><input id=dp2keep2 type=checkbox>' + "Remember password" + '</label>';
- }
- x += '</div>';
- if (j.askkeypass) {
- x += '<div id=d2keyauth2 style=display:none>';
- x += addHtmlValue("Password", '<input type=password id=dp2keypass2 style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- x += '</div>';
- }
- setDialogMode(2, "Authentication", 11, func, x, 'ssh');
- Q('dp2user').focus();
- sshAuthUpdate();
- setTimeout(sshAuthUpdate, 50);
- }
- function sshTunnelUpdate(data) {
- if (typeof data == 'string') {
- if (data[0] == '{') {
- var j = JSON.parse(data);
- switch (j.action) {
- case 'sshauth': {
- sshTunnelAuthDialog(j, sshConnectEx);
- /*
- var x = '';
- x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>')
- x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- x += '<div id=d2passauth>';
- x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- x += '</div><div id=d2keyauth style=display:none>';
- x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:150px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />');
- x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />');
- x += '</div>';
- x += '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>';
- x += '<div id=d2keyauth2 style=font-size:x-small><br />' + "Key file must be in OpenSSH format." + '</div>';
- setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh');
- setTimeout(sshAuthUpdate, 50);
- */
- break;
- }
- case 'sshautoauth': {
- terminal.socket.send(JSON.stringify({ action: 'sshautoauth', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
- break;
- }
- case 'autherror': { p12setConsoleMsg("Authentication Error", 5000); break; }
- case 'sessionerror': { p12setConsoleMsg("Session expired", 5000); break; }
- case 'sessiontimeout': { p12setConsoleMsg("Session timeout", 5000); break; }
- }
- } else if (data[0] == '~') {
- if (xterm.writeUtf8) { xterm.writeUtf8(data.substring(1)); } else { xterm.write(data.substring(1)); }
- }
- }
- }
- /*
- function sshAuthUpdate(e) {
- QV('d2passauth', Q('dp2authmethod').value == 1);
- QV('d2keyauth', Q('dp2authmethod').value == 2);
- QV('d2keyauth2', Q('dp2authmethod').value == 2);
- if (Q('dp2authmethod').value == 1) {
- QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0));
- } else {
- QE('idx_dlgOkButton', false);
- var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000);
- if (ok == true) {
- var reader = new FileReader();
- reader.onload = function (e) {
- var validkey =
- ((e.target.result.indexOf('-----BEGIN OPENSSH PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END OPENSSH PRIVATE KEY-----') >= 0)) ||
- ((e.target.result.indexOf('-----BEGIN RSA PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END RSA PRIVATE KEY-----') >= 0));
- QE('idx_dlgOkButton', validkey);
- }
- reader.readAsText(Q('dp2key').files[0]);
- }
- }
- }
- function sshConnectEx(b) {
- if (b == 0) {
- if (terminal != null) { connectTerminal(); } // Disconnect
- } else {
- if (Q('dp2authmethod').value == 1) {
- terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
- } else {
- var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value, keep = Q('dp2keep').checked;
- reader.onload = function (e) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); }
- reader.readAsText(Q('dp2key').files[0]);
- }
- }
- }
- */
- function sshAuthUpdate(e) {
- QV('d2userauth', Q('dp2authmethod').value != 3);
- QV('d2passauth', Q('dp2authmethod').value == 1);
- QV('d2keyauth', Q('dp2authmethod').value == 2);
- QV('d2keyauth2', Q('dp2authmethod').value == 3);
- if (Q('dp2authmethod').value == 1) {
- QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0));
- } else if (Q('dp2authmethod').value == 3) {
- QE('idx_dlgOkButton', Q('dp2keypass2').value.length > 0);
- } else {
- QE('idx_dlgOkButton', false);
- if ((features2 & 0x00400000) == 0) { QE('dp2keep2', Q('dp2keep1').checked); }
- var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000);
- if (ok == true) {
- var reader = new FileReader();
- reader.onload = function (e) {
- var validkey =
- ((e.target.result.indexOf('-----BEGIN OPENSSH PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END OPENSSH PRIVATE KEY-----') >= 0)) ||
- ((e.target.result.indexOf('-----BEGIN RSA PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END RSA PRIVATE KEY-----') >= 0));
- QE('idx_dlgOkButton', validkey);
- QS('d2badkey')['color'] = validkey ? '#000' : '#F00';
- }
- reader.readAsText(Q('dp2key').files[0]);
- }
- }
- // When the enter key is pressed, move to the next field
- if (e && (e.keyCode == 13) && (e.target) && (Q('dp2authmethod').value == 1)) {
- if (e.target.id == 'dp2user') { Q('dp2pass').focus(); }
- if (e.target.id == 'dp2pass') { dialogclose(1); }
- }
- }
- function sshConnectEx(b) {
- if (b == 0) {
- if (terminal != null) { connectTerminal(); } // Disconnect
- } else {
- var keep = 0;
- if (Q('dp2authmethod').value == 1) {
- if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
- terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
- } else if (Q('dp2authmethod').value == 3) {
- terminal.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
- } else {
- if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
- var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
- reader.onload = function (e) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); }
- reader.readAsText(Q('dp2key').files[0]);
- }
- }
- }
- // Send the new terminal size to the agent
- function xTermSendResize() {
- xtermResizeTimer = null;
- if ((xterm != null) && (terminal != null) && (terminal.sendCtrlMsg != null)) {
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- terminal.socket.send(JSON.stringify({ action: 'resize', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight }));
- } else {
- terminal.sendCtrlMsg(JSON.stringify({ ctrlChannel: '102938', type: 'termsize', cols: xterm.cols, rows: xterm.rows }));
- }
- }
- }
- function connectTerminal(e, contype, options) {
- p12clearConsoleMsg();
- if (!terminal) {
- // Terminal setup
- var termoptions = { protocol: ((options != null) && (typeof options.protocol == 'number')) ? options.protocol : 1 };
- if (options && options.requireLogin) { termoptions.requireLogin = true; }
- /*
- if ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) == -1) {
- if (Q('termSizeList').value == 1) { termoptions.cols = 80; termoptions.rows = 25; termoptions.xterm = true; }
- else if (Q('termSizeList').value == 2) { termoptions.cols = 100; termoptions.rows = 30; termoptions.xterm = true; }
- else if (Q('termSizeList').value == 3) {
- // TODO: Try to improve terminal auto-size.
- termoptions.cols = Math.floor((Q('column_l').clientWidth - 60) / 10);
- termoptions.rows = Math.floor((Q('column_l').clientHeight - 120) / 20);
- termoptions.xterm = true;
- }
- }
- // If shift is pressed
- if ((e && (e.shiftKey == true))) {
- if (currentNode.agent.id > 4) {
- if (termoptions.protocol == 1) { termoptions.protocol = 7; } // Switch to user shell
- } else {
- if (termoptions.protocol == 1) { termoptions.protocol = 6; } // Switch to Powershell
- }
- }
- */
- // If the server requires a shell type
- if ((serverinfo.linuxshell) != null && (currentNode.agent.id > 4)) {
- if (serverinfo.linuxshell == 'root') { termoptions.protocol = 1; delete termoptions.requireLogin; }
- if (serverinfo.linuxshell == 'user') { termoptions.protocol = 8; delete termoptions.requireLogin; }
- if (serverinfo.linuxshell == 'login') { termoptions.protocol = 1; termoptions.requireLogin = true; }
- }
- // Setup a mesh agent xterm terminal
- QV('termarea3xdiv', true);
- // Setup the terminal with auto-fit
- if (xterm != null) { xterm.dispose(); }
- xterm = new Terminal();
- xtermfit = new FitAddon.FitAddon();
- if (xtermfit) { xterm.loadAddon(xtermfit); }
- //xterm.setOption('scrollback', 0);
- //xterm.setOption('fontSize', 15);
- xterm.open(Q('termarea3xdiv'));
- xterm.onData(function (data) { if (terminal.urlname == 'sshterminalrelay.ashx') { terminal.socket.send('~' + data); } else { terminal.sendText(data); } })
- if (xtermfit) { xtermfit.fit(); }
- xterm.onResize(function (size) {
- // Despam resize
- if (xtermResizeTimer) clearTimeout(xtermResizeTimer);
- xtermResizeTimer = setTimeout(xTermSendResize, 200);
- });
- // Remove terminal textarea and scrollbar.
- document.getElementsByClassName('xterm-helper-textarea')[0].onfocus = () => { xterm.blur(); if (!fullscreen) toggleKeyboard(); };
- document.getElementsByClassName('xterm-viewport')[0].style.overflow = 'hidden';
- // Setup a terminal tunnel to the agent
- terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((contype == 3) ? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
- if (contype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
- terminal.debugmode = debugmode;
- terminal.m.debugmode = debugmode;
- terminal.options = termoptions;
- terminal.options = { cols: xterm.cols, rows: xterm.rows };
- if (termoptions.requireLogin) { terminal.options.requireLogin = true; }
- terminal.Start(terminalNode._id);
- terminal.onStateChanged = onTerminalStateChange;
- terminal.contype = contype;
- terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it.
- terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); }
- } else {
- terminal.Stop();
- terminal = null;
- if (fullscreen) { deskToggleFull(); }
- }
- Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
- }
- function termSendKey(key, id) {
- if (!terminal || xxdialogMode) return;
- if (xterm != null) {
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + String.fromCharCode(key));
- } else if (terminal.sendText) {
- // MeshAgent
- terminal.sendText(String.fromCharCode(key));
- } else {
- // CIRA
- terminal.send(String.fromCharCode(key));
- }
- xterm.focus();
- } else if (terminal != null) {
- terminal.m.TermSendKey(key);
- Q(id).blur(); // Deselect the connect button so the button does not get key presses.
- }
- }
- //
- // Terminal Shortcut Keys
- //
- function updateTermShortcutKeys() {
- var x = '';
- for (var i = 64; i <= 95; i++) { x += '<div class="menuButton" style="width:70px" onclick="termMenuButton(' + i + ')">' + "Ctrl + " + String.fromCharCode(i) + '</div>'; }
- QH('termButtonMenu', x);
- }
- function termMenuButton(c) {
- toggleMenu(true);
- if (terminal.urlname == 'sshterminalrelay.ashx') {
- // SSH
- terminal.socket.send('~' + String.fromCharCode(c - 64));
- } else {
- // Agent
- terminal.sendText(String.fromCharCode(c - 64));
- }
- }
- //
- // FILES
- //
- var filesNode;
- function setupFiles() {
- // Setup the files tab
- var samenode = (filesNode == currentNode);
- filesNode = currentNode;
- var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal
- QE('p13Connect', online);
- QE('p13Connects', online);
- QV('p13Connect', (files == null) && (filesNode.mtype == 2));
- QV('p13Connects', (files == null) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
- QV('p13Disconnect', files != null);
- if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
- p13setActions();
- }
- function onFilesStateChange(xfiles, state) {
- setSessionActivity();
- QV('p13Connect', (state == 0) && (filesNode.mtype == 2));
- QV('p13Connects', (state == 0) && (filesNode.agent != null) && (filesNode.agent.id != 3) && (filesNode.agent.id != 4));
- QV('p13Disconnect', state != 0);
- var str = StatusStrs[state];
- if (state == 3) {
- if (files.contype == 2) { str += ", SFTP"; }
- if (files.webRtcActive == true) { str += ", WebRTC"; }
- }
- Q('p13Status').textContent = str;
- switch (state) {
- case 0:
- // Disconnected, clear the files
- QH('p13files', '');
- p13filetree = null;
- p13filetreelocation = [];
- QH('p13currentpath', '');
- QE('p13FolderUp', false);
- p13setActions();
- if (files != null) { files.Stop(); files = null; }
- if (uploadFile != null) { p13uploadFileTransferDone(); uploadFile = null; }
- break;
- case 3:
- p13filetreelocation = [];
- p13targetpath = '';
- if (files) {
- var filepaths = [];
- try { filepaths = JSON.parse(getstore('_devFilePaths', '[]')); } catch (ex) { }
- for (var i = 0; i < filepaths.length; i++) { if (filepaths[i].n == currentNode._id) { p13targetpath = filepaths[i].p; } }
- p13filetreelocation = p13targetpath.split('/');
- files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
- //if (files.serverIsRecording == true) { QV('filesRecordIcon', true); }
- }
- break;
- default:
- //console.log('Unknown onFilesStateChange state', state);
- break;
- }
- }
- function CreateRemoteFiles(onFileUpdate) {
- var obj = { protocol: 5 };
- obj.onFileUpdate = onFileUpdate;
- obj.xxStateChange = function (state) { }
- obj.ProcessData = function (data) { obj.onFileUpdate(data); }
- return obj;
- }
- // Debug Only
- var autoConnectFilesTimer = null;
- function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } }
- function connectFiles(e, contype) {
- p13clearConsoleMsg();
- if (!files) {
- // Setup a mesh agent files
- files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
- if (contype == 2) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay.
- files.contype = contype;
- files.attemptWebRTC = attemptWebRTC;
- files.webrtcconfig = webrtcconfiguration;
- files.onStateChanged = onFilesStateChange;
- files.onConsoleMessageChange = function () {
- if (files.consoleMessage) {
- Q('p13FilesConsoleMsg').innerHTML += formatAgentConsoleMessage(files.consoleMessage, files.consoleMessageId, files.consoleMessageArgs);
- QV('p13FilesConsoleMsg', true);
- if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); }
- if (files.consoleMessageTimeout) { p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, files.consoleMessageTimeout * 1000); }
- } else {
- p13clearConsoleMsg();
- }
- }
- files.Start(filesNode._id);
- } else {
- //QH('Term', '');
- files.Stop();
- files = null;
- }
- p13clipboard = p13clipboardFolder = null;
- p13clipboardCut = 0;
- p13updateClipview();
- }
- var p13filetree = null;
- var p13targetpath = null;
- var p13filetreelocation = [];
- function p13gotFiles(data) {
- if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } // This is ok because 4 first bytes is a control value.
- //console.log('p13gotFiles', data);
- try { data = JSON.parse(decode_utf8(data)); } catch (ex) { data = JSON.parse(data); }
- if (data.action == 'download') { p13gotDownloadCommand(data); return; }
- // Process any SSH actions
- switch (data.action) {
- case 'sshauth': { sshTunnelAuthDialog(data, p13sshConnectEx); break; }
- case 'autherror': { p13setConsoleMsg("Authentication Error", 5000); return; }
- case 'connectionerror': { p13setConsoleMsg("Connection Error", 5000); return; }
- case 'sessionerror': { p13setConsoleMsg("Session expired", 5000); return; }
- case 'sessiontimeout': { p13setConsoleMsg("Session timeout", 5000); return; }
- }
- // Process file upload commands
- if ((data.action != null) && (data.action.startsWith('upload'))) { p13gotUploadData(data); return; }
- if (data.path != null) {
- if (data.dir == null) {
- if (p13targetpath != '') { p13folderup(); }
- } else {
- data.path = data.path.replace(/\//g, '\\');
- if ((p13filetree != null) && (data.path == p13filetree.path)) {
- // This is an update to the same folder
- var checkedNames = p13getCheckedNames();
- p13filetree = data;
- p13updateFiles(checkedNames);
- } else {
- // Make both paths use the same seperator not start with /
- var x1 = data.path.replace(/\//g, '\\'), x2 = p13targetpath.replace(/\//g, '\\');
- while ((x1.length > 0) && (x1[0] == '\\')) { x1 = x1.substring(1); }
- while ((x2.length > 0) && (x2[0] == '\\')) { x2 = x2.substring(1); }
- if ((x1 == x2) || ((data.path == '\\') && (p13targetpath == ''))) {
- // This is a different folder
- p13filetree = data;
- p13updateFiles();
- }
- }
- }
- }
- }
- function p13sshConnectEx(b) {
- if (b == 0) {
- if (files != null) { connectFiles(); } // Disconnect
- } else {
- var keep = 0;
- if (Q('dp2authmethod').value == 1) {
- if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep').checked ? 1 : 0); }
- files.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: keep }));
- } else if (Q('dp2authmethod').value == 3) {
- files.socket.send(JSON.stringify({ action: 'sshkeyauth', keypass: Q('dp2keypass2').value }));
- } else {
- if ((features2 & 0x00400000) == 0) { keep = (Q('dp2keep1').checked ? 1 : 0); if (keep == 1) { keep += (Q('dp2keep2').checked ? 1 : 0); } } // Keep: 1 = user & key, 2 = User, key and password
- var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value;
- reader.onload = function (e) { files.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep })); }
- reader.readAsText(Q('dp2key').files[0]);
- }
- }
- }
- function p13getCheckedNames() {
- // Save all existing checked boxes
- var checkedNames = [], checkboxes = document.getElementsByName('fd');
- for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedNames.push(p13filetree.dir[checkboxes[i].value].n) }; }
- return checkedNames;
- }
- function p13updateFiles(checkedNames) {
- var html1 = '', html2 = '', displayPath = '<a style=cursor:pointer;color:black onclick=p13folderup(0)>' + "Root" + '</a>', fullPath = 'Root';
- // Work on parsing the file path
- var x = p13filetree.path.split('\\');
- p13filetreelocation = [];
- for (var i in x) { if (x[i] != '') { p13filetreelocation.push(x[i]); } } // Remove empty spaces
- for (var i in p13filetreelocation) { displayPath += ' / <a style=cursor:pointer;color:black onclick=p13folderup(' + (parseInt(i) + 1) + ')>' + EscapeHtml(p13filetreelocation[i]) + '</a>' } // Setup the path we display
- var newlinkpath = p13filetreelocation.join('/');
- // Sort the files
- var filetreexx = p13sort_files(p13filetree.dir);
- // Display all files and folders at this location
- for (var i in filetreexx) {
- // Figure out the name and shortname
- var f = filetreexx[i], name = f.n, shortname;
- // if (name.length > 40) { shortname = EscapeHtml(name.substring(0, 70)) + "..."; } else { shortname = EscapeHtml(name); }
- // Removed redundant filename length check because we handle it in the CSS
- shortname = EscapeHtml(name);
- // Figure out the size
- var fsize = '';
- if (f.s != null) { fsize = getFileSizeStr(f.s); }
- var h = '';
- if (f.t < 3) {
- var right = '';
- h = '<div class=filelist file=999><input file=999 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span style=float:right>' + right + '</span><span title="' + shortname + '"><div class=fileIcon' + f.t + '></div><a style=cursor:pointer onclick=p13folderset("' + encodeURIComponent(f.nx) + '")>' + shortname + '</a></span></div>';
- } else {
- var link = shortname;
- if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" style=cursor:pointer onclick=\"p13downloadfile(\'' + encodeURIComponent(newlinkpath + '/' + name) + '\',\'' + encodeURIComponent(name) + '\',' + f.s + ')\">' + shortname + '</a>'; }
- h = '<div class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span style=float:right;padding-right:4px>' + fsize + '</span><span title="' + shortname + '"><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
- }
- if (f.t < 3) { html1 += h; } else { html2 += h; }
- }
- // Display the files and path
- QH('p13files', html1 + html2);
- QH('p13currentpath', displayPath);
- QE('p13FolderUp', p13filetreelocation.length != 0);
- // Re-check all boxes if needed using names
- if (checkedNames != null) { var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkedNames.indexOf(p13filetree.dir[checkboxes[i].value].n) >= 0) { checkboxes[i].checked = true; } } }
- // Update the actions buttons
- p13setActions();
- }
- function p13folderset(x) {
- p13targetpath = joinPaths(p13filetree.path, p13filetree.dir[x].n).split('\\').join('/');
- if (files) {
- p13storeCurrentPath(p13targetpath);
- files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
- }
- }
- function p13folderup(x) {
- if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } }
- p13targetpath = p13filetreelocation.join('/');
- if (files) {
- p13storeCurrentPath(p13targetpath);
- files.sendText({ action: 'ls', reqid: 1, path: p13targetpath });
- }
- }
- // Store the current path for a given node as browser state.
- // This is so, when reconnecting to a device, you go back to the same path.
- function p13storeCurrentPath(path) {
- var filepaths = [], j = -1;
- try { filepaths = JSON.parse(getstore('_devFilePaths', '[]')); } catch (ex) { }
- for (var i = 0; i < filepaths.length; i++) { if (filepaths[i].n == currentNode._id) { j = i; } }
- if (j >= 0) { filepaths.splice(j, 1); }
- filepaths.push({ n: currentNode._id, p: path });
- while (filepaths.length > 40) { filepaths.shift(); } // Keep only 40 devices worth of paths.
- putstore('_devFilePaths', JSON.stringify(filepaths));
- }
- var p13sortorder;
- function p13sort_filename(a, b) { if (a.ln > b.ln) return (1 * p13sortorder); if (a.ln < b.ln) return (-1 * p13sortorder); return 0; }
- function p13sort_timestamp(a, b) { if (a.d > b.d) return (1 * p13sortorder); if (a.d < b.d) return (-1 * p13sortorder); return 0; }
- function p13sort_bysize(a, b) { if (a.s == b.s) return p13sort_filename(a, b); return (((a.s - b.s)) * p13sortorder); }
- function p13sort_files(files) {
- var r = [], sortselection = Q('p13sortdropdown').value;
- for (var i in files) { files[i].nx = i; if (files[i].s == null) { files[i].s = 0; } if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); }
- p13sortorder = 1;
- if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; }
- if (sortselection == 1) { r.sort(p13sort_filename); }
- else if (sortselection == 2) { r.sort(p13sort_bysize); }
- else if (sortselection == 3) { r.sort(p13sort_timestamp); }
- return r;
- }
- function p13setActions() {
- var advancedFeatures = ((currentNode.agent) && (currentNode.agent.id != 14)); // Reduct file feature on some devices.
- if (p13filetree == null) {
- QE('p13DeleteFileButton', false);
- QE('p13NewFolderButton', false);
- QE('p13UploadButton', false);
- QE('p13RenameFileButton', false);
- QE('p13SelectAllButton', false);
- Q('p13SelectAllButton').value = "All";
- QE('p13RefreshButton', false);
- QE('p13CutButton', false);
- QE('p13CopyButton', false);
- QE('p13PasteButton', false);
- } else {
- var cc = p13getFileSelCount(), tc = p13getFileCount(), sfc = p13getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders)
- var winAgent = isWindowsNode(currentNode);
- QE('p13DeleteFileButton', advancedFeatures && (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13NewFolderButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13UploadButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13RenameFileButton', advancedFeatures && (cc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13SelectAllButton', tc > 0);
- Q('p13SelectAllButton').value = (cc > 0 ? "None" : "All");
- QE('p13RefreshButton', true);
- QE('p13CutButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13CopyButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)));
- QE('p13PasteButton', advancedFeatures && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
- }
- var filesState = ((files != null) && (files.state != 0));
- if (((filesState == true) && (files.contype != 2)) || (filesNode.agent == null) || (filesNode.agent.id == 3) || (filesNode.agent.id == 4)) {
- QH('filesCustomUpperRight', '');
- } else {
- QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport ? filesNode.sshport : 22)) + '</a>');
- }
- QV('filesActionsBtn', filesNode.mtype != 3);
- }
- function p13getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; }
- function p13getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; }
- function p13getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fd'); return checkboxes.length; }
- function p13selectallfile() { var nv = (p13getFileSelCount() == 0), checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p13setActions(); }
- function p13createfolder() { setDialogMode(2, "New Folder", 3, p13createfolderEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% />'); focusTextBox('p13renameinput'); p13fileNameCheck(); }
- function p13createfolderEx() { files.sendText({ action: 'mkdir', reqid: 1, path: p13filetreelocation.join('/') + '/' + Q('p13renameinput').value }); p13folderup(999); }
- function p13deletefile() { var cc = p13getFileSelCount(), rec = (p13getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p13recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p13recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p13deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); }
- function p13deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(p13filetree.dir[checkboxes[i].value].n); } } files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: delfiles, rec: Q('p13recdeleteinput').checked }); p13folderup(999); }
- function p13renamefile() { var renamefile, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = p13filetree.dir[checkboxes[i].value].n; } } setDialogMode(2, "Rename", 3, p13renamefileEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'rename', path: p13filetreelocation.join('/'), oldname: renamefile }); focusTextBox('p13renameinput'); p13fileNameCheck(); }
- function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); }
- function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } }
- function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); updateUploadDialogOk('p13uploadinput'); }
- function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); }
- function p13viewfile() {
- var checkboxes = document.getElementsByName('fd');
- for (var i = 0; i < checkboxes.length; i++) {
- if (checkboxes[i].checked) {
- if (p13filetree.dir[checkboxes[i].value].s <= 204800) {
- p13downloadfile(encodeURIComponent(p13filetreelocation.join('/') + '/' + p13filetree.dir[checkboxes[i].value].n), encodeURIComponent(p13filetree.dir[checkboxes[i].value].n), p13filetree.dir[checkboxes[i].value].s, 'viewer');
- } else { messagebox("File Editor", "Only files less than 200k can be edited."); }
- break;
- }
- }
- }
- var p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0;
- function p13copyFile(cut) { var checkboxes = document.getElementsByName('fd'); p13clipboard = []; p13clipboardCut = cut, p13clipboardFolder = p13targetpath; for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p13clipboard.push(p13filetree.dir[checkboxes[i].value].n); } } p13updateClipview(); }
- function p13pasteFile() {
- var x = '';
- if ((p13clipboard != null) && (p13clipboard.length > 0)) {
- if (p13clipboardCut == 0) {
- if (p13clipboard.length > 1) { x = format("Confirm copy of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm copy of 1 entrie to this location?"); }
- } else {
- if (p13clipboard.length > 1) { x = format("Confirm move of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm move of 1 entrie to this location?"); }
- }
- }
- setDialogMode(2, "Paste", 3, p13pasteFileEx, x);
- }
- function p13pasteFileEx() { files.sendText({ action: (p13clipboardCut == 0 ? 'copy' : 'move'), reqid: 1, scpath: p13clipboardFolder, dspath: p13targetpath, names: p13clipboard }); p13folderup(999); if (p13clipboardCut == 1) { p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0; p13updateClipview(); } }
- function p13updateClipview() {
- var x = '';
- if ((p13clipboard != null) && (p13clipboard.length > 0)) {
- if (p13clipboardCut == 0) {
- if (p13clipboard.length > 1) {
- x = format("Holding {0} entries for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
- } else {
- x = format("Holding 1 entrie for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
- }
- } else {
- if (p13clipboard.length > 1) {
- x = format("Holding {0} entries for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length);
- } else {
- x = format("Holding 1 entrie for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.');
- }
- }
- }
- QH('p13bottomstatus', x);
- p13setActions();
- }
- function p13clearClip() { p13clipboard = null; p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); return false; } function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); }
- function getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == "3"))) cc++; } return cc; }
- function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; }
- //
- // FILES DOWNLOAD
- //
- var downloadFile; // Global state for file download
- // Called by the html page to start a download, arguments are: path, file name and file size.
- function p13downloadfile(x, y, z) {
- if (xxdialogMode || downloadFile || !files) return;
- downloadFile = { path: decodeURIComponent(x), file: decodeURIComponent(y), size: z, tsize: 0, data: '', state: 0, id: Math.random() }
- //console.log('p13downloadFileCancel', downloadFile);
- files.sendText({ action: 'download', sub: 'start', id: downloadFile.id, path: downloadFile.path });
- setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + EscapeHtml(downloadFile.file) + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />');
- }
- // Called by the html page to cancel the download
- function p13downloadFileCancel() { setDialogMode(0); files.sendText({ action: 'download', sub: 'cancel', id: downloadFile.id }); downloadFile = null; }
- // Called by the transport when download control command is received
- function p13gotDownloadCommand(cmd) {
- //console.log('p13gotDownloadCommand', cmd);
- if ((downloadFile == null) || (cmd.id != downloadFile.id)) return;
- if (cmd.sub == 'start') { downloadFile.state = 1; files.sendText({ action: 'download', sub: 'startack', id: downloadFile.id }); }
- else if (cmd.sub == 'cancel') { downloadFile = null; setDialogMode(0); }
- }
- // Called by the transport when binary data is received
- function p13gotDownloadBinaryData(data) {
- if (!downloadFile || downloadFile.state == 0) return;
- if (data.length > 4) {
- downloadFile.tsize += (data.length - 4); // Add to the total bytes received
- downloadFile.data += data.substring(4); // Append the data
- Q('d2progressBar').value = downloadFile.tsize; // Change the progress bar
- }
- if ((ReadInt(data, 0) & 1) != 0) { // Check end flag
- saveAs(data2blob(downloadFile.data), downloadFile.file); downloadFile = null; setDialogMode(0); // Save the file
- } else {
- files.sendText({ action: 'download', sub: 'ack', id: downloadFile.id }); // Send the ACK
- }
- }
- /*
- var downloadFile; // Global state for file download
- // Called by the html page to start a download, arguments are: path, file name and file size.
- function p13downloadfile(x, y, z) {
- if (xxdialogMode) return;
- downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport
- downloadFile.ctrlMsgAllowed = false;
- downloadFile.onStateChanged = onFileDownloadStateChange;
- downloadFile.xpath = decodeURIComponent(x);
- downloadFile.xfile = decodeURIComponent(y);
- downloadFile.xsize = z;
- downloadFile.xtsize = 0;
- downloadFile.xstate = 0;
- downloadFile.Start(filesNode._id);
- setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + downloadFile.xfile + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />');
- }
- // Called by the html page to cancel the download
- function p13downloadFileCancel(button, tag) {
- //console.log('p13downloadFileCancel');
- downloadFile.Stop();
- delete downloadFile;
- downloadFile = null;
- }
- // Called by the file transport to indicate when the transport connection state has changed
- function onFileDownloadStateChange(xdownloadFile, state) {
- switch (state) {
- case 0: // Transport as disconnected. If this is not part of an abort, we need to save the file
- setDialogMode(0); // Close any dialog boxes if present
- if ((downloadFile != null) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file
- break;
- case 3: // Transport as connected, send a command to indicate we want to start a file download
- downloadFile.send(JSON.stringify({ action: 'download', reqid: 1, path: downloadFile.xpath }));
- break;
- default:
- console.log('Unknown onFileDownloadStateChange state', state);
- break;
- }
- }
- // Called by the transport when data is received
- function p13gotDownloadData(data) {
- if (downloadFile.xstate == 0) { // If state is 0, this is a command confirming if the file will be transfered.
- var cmd = JSON.parse(data);
- if (cmd.action == 'downloadstart') { // Yes, the file is about to start
- downloadFile.xstate = 1; // Switch to state 1, we will start receiving the file data
- downloadFile.xdata = ''; // Start with empty data
- downloadFile.send('a'); // Send the first ACK
- } else if (cmd.action == 'downloaderror') { // Problem opening this file, cancel
- p13downloadFileCancel();
- }
- } else { // We are in the process of receiving the file
- downloadFile.xtsize += (data.length); // Add to the total bytes received
- downloadFile.xdata += data; // Append the data
- Q('d2progressBar').value = downloadFile.xtsize; // Change the progress bar
- downloadFile.send('a'); // Send the ACK
- }
- }
- */
- //
- // FILES UPLOAD
- //
- var uploadFile;
- function p13doUploadFiles(files) {
- if (xxdialogMode) return;
- // Check if we are going to overwrite any files
- var winAgent = isWindowsNode(currentNode);
- var targetFiles = [], overWriteCount = 0;
- for (var i in p13filetree.dir) { if (winAgent) { targetFiles.push(p13filetree.dir[i].n.toLowerCase()); } else { targetFiles.push(p13filetree.dir[i].n); } }
- for (var i = 0; i < files.length; i++) {
- if (winAgent) {
- if (targetFiles.indexOf(files[i].name.toLowerCase()) >= 0) { overWriteCount++; }
- } else {
- if (targetFiles.indexOf(files[i].name) >= 0) { overWriteCount++; }
- }
- }
- if (overWriteCount == 0) {
- // If no overwrite, go ahead with upload
- p13uploadFileContinue(1, files);
- } else {
- // Otherwise, prompt for confirmation
- setDialogMode(2, "Upload File", 3, p13uploadFileContinue, format((overWriteCount == 1) ? "Upload will overwrite 1 file. Continue?" : "Upload will overwrite {0} files. Continue?", overWriteCount), files);
- }
- }
- function p13uploadFileContinue(b, files) {
- uploadFile = {};
- uploadFile.xpath = p13filetreelocation.join('/');
- uploadFile.xfiles = files;
- uploadFile.xfilePtr = -1;
- setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '<div id=p13dfileName>' + "Connecting..." + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=0 />');
- p13uploadNextFile();
- }
- // Perform SHA-384 hashing
- const byteToHex = [];
- for (var n = 0; n <= 0xff; ++n) { var hexOctet = n.toString(16).padStart(2, '0'); byteToHex.push(hexOctet); }
- function arrayBufferToHex(arrayBuffer) { return Array.prototype.map.call(new Uint8Array(arrayBuffer), n => byteToHex[n]).join(''); }
- function performHash(data, f) { window.crypto.subtle.digest('SHA-384', data).then(function (v) { f(arrayBufferToHex(v)); }, function () { f(null); }); }
- function performHashOnFile(file, f) {
- // TODO: At some point, try to make this work for files of unlimited size using a digest stream
- var reader = new FileReader();
- reader.onerror = function (err) { f(null); }
- reader.onload = function () { window.crypto.subtle.digest('SHA-384', reader.result).then(function (v) { f(arrayBufferToHex(v)); }, function () { f(null); }); };
- reader.readAsArrayBuffer(file);
- }
- // Push the next file
- function p13uploadNextFile() {
- uploadFile.xfilePtr++;
- if (uploadFile.xfiles.length > uploadFile.xfilePtr) {
- uploadFile.xptr = 0;
- var file = uploadFile.xfiles[uploadFile.xfilePtr];
- QH('p13dfileName', EscapeHtml(file.name));
- Q('d2progressBar').max = file.size;
- Q('d2progressBar').value = 0;
- if (file.xdata == null) {
- uploadFile.xfile = file;
- // If the remote file already exists and is smaller then our file, see if we can resume the trasfer
- var f = null;
- for (var i in p13filetree.dir) { if (p13filetree.dir[i].n == file.name) { f = p13filetree.dir[i]; } }
- if ((f != null) && (f.s <= uploadFile.xfile.size)) {
- performHashOnFile(uploadFile.xfile, function (hash) { files.sendText(JSON.stringify({ action: 'uploadhash', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, tag: { h: hash.toUpperCase(), s: f.s, skip: f.s == uploadFile.xfile.size } })); });
- } else {
- files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size }));
- }
- } else {
- // Data already loaded
- uploadFile.xdata = file.xdata;
- files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
- }
- } else {
- p13uploadFileTransferDone();
- }
- }
- // Used to cancel the entire transfer.
- function p13uploadFileCancel(button, tag) {
- if (uploadFile != null) { files.sendText(JSON.stringify({ action: 'uploadcancel', reqid: uploadFile.xfilePtr })); uploadFile = null; }
- p13uploadFileTransferDone();
- }
- // Used to cancel the entire transfer.
- function p13uploadFileTransferDone() {
- uploadFile = null; // No more files to upload, clean up.
- setDialogMode(0); // Close the dialog box
- p13folderup(9999); // Refresh the current folder
- }
- // Receive upload ack from the mesh agent, use this to keep sending more data
- function p13gotUploadData(cmd) {
- if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
- switch (cmd.action) {
- case 'uploadstart': { uploadFile.xdataPriming = 8; p13uploadNextPart(false); break; } // Send 8 more blocks of 16k to fill the websocket.
- case 'uploadack': { p13uploadNextPart(false); break; }
- case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; }
- case 'uploaderror': { p13uploadFileCancel(); break; }
- case 'uploadhash': {
- var file = uploadFile.xfiles[uploadFile.xfilePtr];
- if (file) {
- if (cmd.tag.h === cmd.hash) {
- if (cmd.tag.skip) {
- p13uploadNextFile();
- } else {
- uploadFile.xptr = cmd.tag.s;
- files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size, append: true }));
- }
- } else {
- files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xfile.size, append: false }));
- }
- }
- break;
- }
- }
- }
- // Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file.
- function p13uploadNextPart(dataPriming) {
- if (uploadFile.xdata) {
- var data = uploadFile.xdata, start = uploadFile.xptr;
- if (start >= data.byteLength) {
- files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr }));
- } else {
- var end = uploadFile.xptr + (attemptWebRTC ? 16384 : 65536);
- if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
- var dataslice = new Uint8Array(data.slice(start, end))
- if ((dataslice[0] == 123) || (dataslice[0] == 0)) {
- var datapart = new Uint8Array(end - start + 1);
- datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command.
- files.send(datapart);
- } else {
- files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON.
- }
- uploadFile.xptr = end;
- Q('d2progressBar').value = end;
- }
- } else if (uploadFile.xfile) {
- if (uploadFile.xreader != null) return; // Data reading already in process
- if (uploadFile.xptr >= uploadFile.xfile.size) return;
- var end = uploadFile.xptr + (attemptWebRTC ? 16384 : 65536);
- if (end > uploadFile.xfile.size) { if (dataPriming == true) { return; } end = uploadFile.xfile.size; }
- uploadFile.xreader = new FileReader();
- uploadFile.xreader.onerror = function (err) { console.log(err); }
- uploadFile.xreader.onload = function () {
- var data = uploadFile.xreader.result;
- delete uploadFile.xreader;
- if (data == null) return;
- var dataslice = new Uint8Array(data)
- if ((dataslice[0] == 123) || (dataslice[0] == 0)) {
- var datapart = new Uint8Array(data.byteLength + 1);
- datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command.
- files.send(datapart);
- } else {
- files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON.
- }
- uploadFile.xptr = end;
- Q('d2progressBar').value = end;
- if (uploadFile.xptr >= uploadFile.xfile.size) {
- files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr }));
- } else {
- if (uploadFile.xdataPriming > 0) { uploadFile.xdataPriming--; p13uploadNextPart(true); }
- }
- };
- uploadFile.xreader.readAsArrayBuffer(uploadFile.xfile.slice(uploadFile.xptr, end));
- }
- }
- //
- // DEVICE DETAILS
- //
- var DeviceDetailsHardware = null;
- var DeviceDetailsNetwork = null;
- var DeviceDetailsNodeId = null;
- function updateDeviceDetails(node, hardware, network) {
- if (currentNode == null) return;
- if (node == null) { node = currentNode; }
- if (currentNode._id != node._id) return;
- if (DeviceDetailsNodeId != node._id) { DeviceDetailsHardware = null; DeviceDetailsNetwork = null; DeviceDetailsNodeId = node._id; }
- if (hardware != null) { DeviceDetailsHardware = hardware; }
- if (network != null) { DeviceDetailsNetwork = network; }
- hardware = DeviceDetailsHardware;
- network = DeviceDetailsNetwork;
- if (hardware == null) { hardware = {}; }
- if (network == null) { network = {}; }
- var sections = [], s = {};
- // Operating System
- var x = '';
- if (node.rname) { x += addDetailItem("Name", EscapeHtml(node.rname), s); }
- if (hardware.windows && hardware.windows.osinfo && hardware.windows.osinfo.Description) { x += addDetailItem("Description", EscapeHtml(hardware.windows.osinfo.Description), s); }
- if (node.osdesc) { x += addDetailItem("Version", EscapeHtml(node.osdesc), s); }
- if (hardware.windows && hardware.windows.osinfo) {
- var m = hardware.windows.osinfo;
- if (m.OSArchitecture) {
- if (m.OSArchitecture.startsWith('32')) { x += addDetailItem("Architecture", "32-bit", s); }
- else if (m.OSArchitecture.startsWith('64')) { x += addDetailItem("Architecture", "64-bit", s); }
- else { x += addDetailItem("Architecture", EscapeHtml(m.OSArchitecture), s); }
- }
- if(m.LastBootUpTime){
- var thedate = {
- year: parseInt(m.LastBootUpTime.substring(0, 4)),
- month: parseInt(m.LastBootUpTime.substring(4, 6)) - 1, // Months are 0-based in JavaScript (0 - January, 11 - December)
- day: parseInt(m.LastBootUpTime.substring(6, 8)),
- hours: parseInt(m.LastBootUpTime.substring(8, 10)),
- minutes: parseInt(m.LastBootUpTime.substring(10, 12)),
- seconds: parseInt(m.LastBootUpTime.substring(12, 14)),
- };
- const date = printDateTime(new Date(thedate.year, thedate.month, thedate.day, thedate.hours, thedate.minutes, thedate.seconds));
- x += addDetailItem("Last Boot Up Time", date);
- }
- if(m.Domain){ x += addDetailItem((m.PartOfDomain ? "Domain" : "Workgroup"), EscapeHtml(m.Domain), s); }
- }
- if(hardware.linux && hardware.linux.LastBootUpTime){
- var lastBootUpTime = new Date(hardware.linux.LastBootUpTime);
- var thedate = {
- year: lastBootUpTime.getFullYear(),
- month: lastBootUpTime.getMonth(),
- day: lastBootUpTime.getDate(),
- hours: lastBootUpTime.getHours(),
- minutes: lastBootUpTime.getMinutes(),
- seconds: lastBootUpTime.getSeconds()
- };
- const date = printDateTime(new Date(thedate.year, thedate.month, thedate.day, thedate.hours, thedate.minutes, thedate.seconds));
- x += addDetailItem("Last Boot Up Time", date);
- }
- if(hardware.darwin && hardware.darwin.LastBootUpTime){
- var lastBootUpTime = new Date(hardware.darwin.LastBootUpTime * 1000); // must times by 1000 even tho timestamp is correct?
- var thedate = {
- year: lastBootUpTime.getFullYear(),
- month: lastBootUpTime.getMonth(),
- day: lastBootUpTime.getDate(),
- hours: lastBootUpTime.getHours(),
- minutes: lastBootUpTime.getMinutes(),
- seconds: lastBootUpTime.getSeconds()
- };
- const date = printDateTime(new Date(thedate.year, thedate.month, thedate.day, thedate.hours, thedate.minutes, thedate.seconds));
- x += addDetailItem("Last Boot Up Time", date);
- }
-
- // Windows Security Central
- if (node.wsc) {
- var y = [];
- if (node.wsc.antiVirus != null) { if (node.wsc.antiVirus == 'OK') { y.push("AV" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("AV" + ' - <span style=color:red>' + "BAD" + '</span>'); } }
- if (node.wsc.autoUpdate != null) { if (node.wsc.autoUpdate == 'OK') { y.push("Update" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("Update" + ' - <span style=color:red>' + "BAD" + '</span>'); } }
- if (node.wsc.firewall != null) { if (node.wsc.firewall == 'OK') { y.push("Firewall" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("Firewall" + ' - <span style=color:red>' + "BAD" + '</span>'); } }
- x += addDetailItem("Windows Security", y.join(', '));
- }
- // Defender for Windows Server
- if(node.defender) {
- var y = [];
- if (node.defender.RealTimeProtection != null) { if (node.defender.RealTimeProtection == true) { y.push("RealTimeProtection" + ' - <span style=color:green>' + "On" + '</span>'); } else { y.push("RealTimeProtection" + ' - <span style=color:red>' + "Off" + '</span>'); } }
- if (node.defender.TamperProtected != null) { if (node.defender.TamperProtected == true) { y.push("TamperProtection" + ' - <span style=color:green>' + "On" + '</span>'); } else { y.push("TamperProtection" + ' - <span style=color:red>' + "Off" + '</span>'); } }
- if (node.defender.AntivirusSignatureVersion != null) { y.push("SignatureVersion" + ' - <span style=color:green>' + EscapeHtml(node.defender.AntivirusSignatureVersion) + '</span>'); }
- if (y.length > 0) x += addDetailItem("Windows Defender", y.join(', '));
- }
- // Antivirus
- if (node.av && node.av.length > 0) {
- var y = [];
- for (var i in node.av) {
- if (node.av[i].product) {
- var avx = EscapeHtml(node.av[i].product);
- if (node.av[i].enabled !== true) { avx += ' - <span style=color:red>' + "Disabled" + '</span>'; }
- if (node.av[i].updated !== true) { avx += ' - <span style=color:red>' + "Out of date" + '</span>'; }
- if ((node.av[i].enabled == true) && (node.av[i].updated == true)) { avx += ' - <span style=color:green>' + "OK" + '</span>'; }
- y.push(avx);
- }
- }
- x += addDetailItem("Antivirus", y.join('<br />'));
- }
- // Active Users
- if (node.users && node.users.length > 0) {
- var u = node.users.map(function(user) {
- return addKeyLinkConditional(EscapeHtml(user), "Locked", (node.lusers && node.lusers.indexOf(user) >= 0));
- }).join(', ');
- x += addDetailItem((node.users.length > 1 ? "Active Users" : "Active User"), u);
- }
- if (x != '') { sections.push({ name: "Operating System", html: x, img: 'software' }); }
- // MeshAgent
- if (node.agent) {
- var x = '';
- if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) {
- var str = '';
- if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; }
- if (node.agent.ver != 0) { str += ' v' + node.agent.ver; }
- if (node.agent.id == 14) { str = node.agent.core; }
- x += addDetailItem("Mesh Agent", str);
- }
- if (node.firstconnect) { x += addDetailItem("First agent connection", printDateTime(new Date(node.firstconnect))); }
- if ((node.conn & 1) != 0) {
- x += addDetailItem("Last agent connection", "Connected now");
- } else {
- if (node.lastconnect) { x += addDetailItem("Last agent connection", printDateTime(new Date(node.lastconnect))); }
- }
- if (node.lastaddr) {
- var splitip = node.lastaddr.split(':');
- if (splitip.length > 2) {
- // IPv6
- x += addDetailItem("Last agent address", node.lastaddr);
- } else {
- // IPv4
- if (isPrivateIP(node.lastaddr)) {
- x += addDetailItem("Last agent address", splitip[0]);
- } else {
- x += addDetailItem("Last agent address", '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a>');
- }
- }
- }
- if (hardware.agentvers != null) {
- if (hardware.agentvers.compileTime) {
- try {
- var d = Date.parse(hardware.agentvers.compileTime)
- x += addDetailItem("Compile time", printDateTime(new Date(d)));
- } catch (ex) { }
- }
- }
- if (hardware.time != null) {
- x += addDetailItem("Last details update", printDateTime(new Date(hardware.time)));
- }
- if (x != '') { sections.push({ name: "Mesh Agent", html: x, img: 'meshagent' }); }
- }
- // Mobile
- if (hardware.mobile) {
- var x = '';
- if (hardware.mobile.brand && hardware.mobile.model) { x += addDetailItem("Model", EscapeHtml(hardware.mobile.brand + ', ' + hardware.mobile.model), s); }
- if (hardware.mobile.device) { x += addDetailItem("Device", EscapeHtml(hardware.mobile.device), s); }
- if (hardware.mobile.bootloader) { x += addDetailItem("Bootloader", EscapeHtml(hardware.mobile.bootloader), s); }
- if (hardware.mobile.id) { x += addDetailItem("Identifier", EscapeHtml(hardware.mobile.id), s); }
- if (hardware.mobile.host) { x += addDetailItem("Hostname", EscapeHtml(hardware.mobile.host), s); }
- if (hardware.mobile.androidapi && hardware.mobile.androidrelease) { x += addDetailItem("Android Version", EscapeHtml(hardware.mobile.androidrelease + ', API Level ' + hardware.mobile.androidapi), s); }
- if (x != '') { sections.push({ name: "Mobile Device", html: x, img: 'mobile' }); }
- }
- // Networking
- if (network.netif2 != null) {
- // Display one network interface for each MAC address
- var x = '';
- x += '<table style=width:100%>';
- for (var i in network.netif2) {
- var m = network.netif2[i];
- if ((Array.isArray(m) == false) || (m.length < 1) || (m[0] == null) || ((typeof m[0].mac == 'string') && (m[0].mac.startsWith('00:00:00:00')))) continue;
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml(i + (m[0].fqdn ? (', ' + m[0].fqdn) : '')) + '</b></div>';
- if (m.desc) { x += addDetailItem("Description", EscapeHtml(m.desc).split('(R)').join('®')); }
- //if (m.dnssuffix) { x += addDetailItem("DNS Suffix", m.dnssuffix); }
- if (typeof m[0].mac == 'string') {
- if (m[0].gatewaymac) {
- x += addDetailItem("MAC Layer", format("MAC: {0}, Gateway: {1}", EscapeHtml(m[0].mac), EscapeHtml(m[0].gatewaymac)));
- } else {
- x += addDetailItem("MAC Layer", format("MAC: {0}", EscapeHtml(m[0].mac)));
- }
- }
- if (typeof m[0].speed == 'number' && (m[0].speed != 9223372036854775807 && m[0].speed > 0)) {
- x += addDetailItem("Interface Speed", format("{0}", getNetworkSpeed(m[0].speed)));
- }
- for (var j = 0; j < m.length; j++) {
- var iplayer = m[j], items = [];
- if (iplayer.address) { items.push(format("IP: {0}", EscapeHtml(iplayer.address))); }
- if (iplayer.netmask) { items.push(format("Mask: {0}", EscapeHtml(iplayer.netmask))); }
- if (iplayer.gateway) { items.push(format("Gateway: {0}", EscapeHtml(iplayer.gateway))); }
- if (items.length > 0) {
- if (iplayer.family == 'IPv4') { x += addDetailItem("IPv4 Layer", items.join(", ")); }
- if (iplayer.family == 'IPv6') { x += addDetailItem("IPv6 Layer", items.join(", ")); }
- }
- }
- x += '</div></td></tr>';
- }
- if (hardware.network && hardware.network.dns) {
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += addDetailItem('<b>' + "DNS Servers" + '</b>', hardware.network.dns.join(", "));
- x += '</div></td></tr>';
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Networking", html: x, img: 'networking' }); }
- }
- // Attribute: Intel AMT
- if (node.intelamt != null) {
- var x = '';
- x += addDetailItem("Version", (node.intelamt.ver) ? ('v' + EscapeHtml(node.intelamt.ver)) : ('<i>' + "Unknown" + '</i>'), s);
- x += addDetailItem("Identifier", (node.intelamt.uuid) ? (EscapeHtml(node.intelamt.uuid)) : ('<i>' + "Unknown" + '</i>'), s);
- var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") };
- var provisioningMode = '';
- if ((node.intelamt.state == 2) && node.intelamt.flags) { if (node.intelamt.flags & 2) { provisioningMode = (', ' + "Client Control Mode (CCM)"); } else if (node.intelamt.flags & 4) { provisioningMode = (', ' + "Admin Control Mode (ACM)"); } }
- x += addDetailItem("Provisioning State", ((node.intelamt.state) ? (provisioningStates[node.intelamt.state]) : ('<i>' + "Unknown" + '</i>')) + provisioningMode, s);
- x += addDetailItem("Security", (node.intelamt.tls == 1) ? "Secured using TLS" : "TLS is not setup", s);
- // Check that the Intel AMT user is setup and there is no warnings (1 = invalid credentials, 8 = trying)
- x += addDetailItem("Admin Credentials", ((node.intelamt.user) == null || (node.intelamt.user == '') || ((node.intelamt.warn != null) && ((node.intelamt.warn & 9) != 0))) ? "Not Known" : "Known", s);
- if (x != '') {
- if ((typeof node.intelamt.sku == 'number') && ((node.intelamt.sku & 16) != 0)) {
- sections.push({ name: "Intel® Standard Manageability (Intel® SM)", html: x, img: 'amt' });
- } else {
- sections.push({ name: "Intel® Active Management Technology (Intel® AMT)", html: x, img: 'amt' });
- }
- }
- }
- if (hardware.identifiers) {
- var x = '', ident = hardware.identifiers;
- // BIOS
- if (ident.bios_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.bios_vendor), s); }
- if (ident.bios_version) { x += addDetailItem("Version", EscapeHtml(ident.bios_version), s); }
- if (ident.bios_serial) { x += addDetailItem("Serial", EscapeHtml(ident.bios_serial), s); }
- if (ident.bios_mode) { x += addDetailItem("Mode", EscapeHtml(ident.bios_mode), s); }
- if (x != '') { sections.push({ name: "BIOS", html: x, img: 'chip' }); }
- // Motherboard
- x = '';
- if (ident.board_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.board_vendor), s); }
- if (ident.board_name) { x += addDetailItem("Name", EscapeHtml(ident.board_name), s); }
- if (ident.board_serial && (ident.board_serial != '')) { x += addDetailItem("Serial", EscapeHtml(ident.board_serial), s); }
- if (ident.board_version) { x += addDetailItem("Version", EscapeHtml(ident.board_version), s); }
- if (ident.product_uuid) { x += addDetailItem("Identifier", EscapeHtml(ident.product_uuid), s); }
- if (ident.cpu_name) { x += addDetailItem("CPU", EscapeHtml(ident.cpu_name).split('(TM)').join('™').split('(R)').join('®'), s); }
- if (ident.gpu_name) { for (var i in ident.gpu_name) { x += addDetailItem("GPU", EscapeHtml(ident.gpu_name[i]).split('(TM)').join('™').split('(R)').join('®'), s); } }
- if (x != '') { sections.push({ name: "Motherboard", html: x, img: 'motherboard' }); }
- // System
- x = '';
- if (ident.chassis_manufacturer) { x += addDetailItem("Manufacturer", EscapeHtml(ident.chassis_manufacturer), s); }
- if (ident.product_name) { x += addDetailItem("Product Name", EscapeHtml(ident.product_name), s); }
- if (ident.chassis_serial) { x += addDetailItem("Serial", EscapeHtml(ident.chassis_serial), s); }
- if (ident.chassis_assettag) { x += addDetailItem("Asset Tag", EscapeHtml(ident.chassis_assettag), s); }
- if (x != '') { sections.push({ name: "System", html: x, img: 'system' }); }
- }
- // TPM
- if (hardware.tpm) {
- var x = '', tpm = hardware.tpm;
- if (tpm.SpecVersion) { x += addDetailItem("SpecVersion", parseFloat(EscapeHtml(tpm.SpecVersion)).toFixed(1), s); }
- if (tpm.ManufacturerId) { x += addDetailItem("Identifier", EscapeHtml(tpm.ManufacturerId), s); }
- if (tpm.ManufacturerVersion) { x += addDetailItem("Version", EscapeHtml(tpm.ManufacturerVersion), s); }
- if (tpm.IsActivated != null) { x += addDetailItem("Activated", (tpm.IsActivated ? "Yes" : "No"), s); }
- if (tpm.IsEnabled != null) { x += addDetailItem("Enabled", (tpm.IsEnabled ? "Yes" : "No"), s); }
- if (tpm.IsOwned != null) { x += addDetailItem("Owned", (tpm.IsOwned ? "Yes" : "No"), s); }
- if (x != '') { sections.push({ name: "TPM", html: x, img: 'tpm' }); }
- }
- // Batteries
- if (hardware.battery) {
- var x = '';
- x += '<table style=width:100%>';
- for (var i in hardware.battery) {
- var battery = hardware.battery[i];
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml(battery.DeviceName ? battery.DeviceName : "Unknown") + '</b></div>';
- if (battery.CycleCount) { x += addDetailItem("Cycle Count", EscapeHtml(battery.CycleCount), s); }
- if (battery.FullChargedCapacity) { x += addDetailItem("Full Charged Capacity", format("{0} mWh", battery.FullChargedCapacity), s); }
- if (battery.EstimatedRuntime) { x += addDetailItem("Estimated Runtime", format("{0} minutes", Math.floor((battery.EstimatedRuntime / 60))), s); }
- if (battery.Chemistry) { x += addDetailItem("Chemistry", EscapeHtml(battery.Chemistry), s); }
- if (battery.DesignedCapacity) { x += addDetailItem("Design Capacity", format("{0} mWh", battery.DesignedCapacity), s); }
- if (battery.ManufactureDate) { x += addDetailItem("Manufacture Date", EscapeHtml(battery.ManufactureDate), s); }
- if (battery.ManufactureName) { x += addDetailItem("Manufacture Name", EscapeHtml(battery.ManufactureName), s); }
- if (battery.SerialNumber) { x += addDetailItem("Serial Number", EscapeHtml(battery.SerialNumber), s); }
- if (battery.ChargeRate) { x += addDetailItem("Charge Rate", format("{0} mW", battery.ChargeRate), s); }
- if (battery.Charging != null) { x += addDetailItem("Charging", (battery.Charging ? "Yes" : "No"), s); }
- if (battery.DischargeRate) { x += addDetailItem("Discharge Rate", format("{0} mW", battery.DischargeRate), s); }
- if (battery.Discharging != null) { x += addDetailItem("Discharging", (battery.Discharging ? "Yes" : "No"), s); }
- if (battery.RemainingCapacity) { x += addDetailItem("Remaining Capacity", format("{0} mWh", battery.RemainingCapacity), s); }
- if (battery.Voltage) { x += addDetailItem("Voltage", format("{0} V", (battery.Voltage / 1000)), s); }
- if (battery.Health) { x += addDetailItem("Health", format("{0} %", battery.Health), s); }
- if (battery.BatteryCharge) { x += addDetailItem("Battery Charge", format("{0} %", battery.BatteryCharge), s); }
- x += '</div>';
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Battery", html: x, img: 'battery'}); }
- }
- if (hardware.windows) {
- if (hardware.windows.memory && (hardware.windows.memory.length > 0)) {
- var x = '';
- // Sort Memory
- hardware.windows.memory.sort(function (a, b) { if (a.BankLabel > b.BankLabel) return 1; if (a.BankLabel < b.BankLabel) return -1; return 0; });
- x += '<table style=width:100%>';
- for (var i in hardware.windows.memory) {
- var m = hardware.windows.memory[i];
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml((m.BankLabel ? m.BankLabel : (m.DeviceLocator ? m.DeviceLocator : 'Unknown'))) + '</b></div>';
- if (m.Capacity && m.Speed) { x += addDetailItem("Capacity / Speed", format("{0} Mb, {1} Mhz", (m.Capacity / 1024 / 1024), m.Speed), s); }
- else if (m.Capacity) { x += addDetailItem("Capacity", format("{0} Mb", (m.Capacity / 1024 / 1024)), s); }
- if (m.PartNumber) { x += addDetailItem("Part Number", EscapeHtml((m.Manufacturer && m.Manufacturer != 'Undefined') ? (m.Manufacturer + ', ') : '') + EscapeHtml(m.PartNumber), s); }
- x += '</div>';
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Memory", html: x, img: 'ram' }); }
- }
- }
- if (hardware.linux) {
- if (hardware.linux.memory && (hardware.linux.memory.Memory_Device.length > 0)) {
- var x = '';
- // Sort Memory
- hardware.linux.memory.Memory_Device.sort(function(a, b) { if (a.Locator > b.Locator) return 1; if (a.Locator < b.Locator) return -1; return 0; });
- x += '<table style=width:100%>';
- for (var i in hardware.linux.memory.Memory_Device) {
- var m = hardware.linux.memory.Memory_Device[i];
- if(m.Size && (m.Size == 'No Module Installed')) continue;
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml((m.Locator ? m.Locator : 'Unknown')) + '</b></div>';
- if (m.Size && m.Speed) { x += addDetailItem("Capacity / Speed", format("{0}, {1}", m.Size, m.Speed), s); }
- else if (m.Size) { x += addDetailItem("Capacity", format("{0}", (m.Size)), s); }
- if (m.PartNumber) { x += addDetailItem("Part Number", EscapeHtml((m.Manufacturer && m.Manufacturer != 'Undefined')?(m.Manufacturer + ', '):'') + EscapeHtml(m.PartNumber), s); }
- x += '</div>';
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Memory", html: x, img: 'ram'}); }
- }
- }
- if (hardware.darwin) {
- if (hardware.darwin.memory && (hardware.darwin.memory.length > 0)) {
- var x = '';
- x += '<table style=width:100%>';
- for (var i in hardware.darwin.memory) {
- var m = hardware.darwin.memory[i];
- if(m.Size && (m.Size == 'No Module Installed')) continue;
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml((m.DeviceLocator ? m.DeviceLocator : 'Unknown')) + '</b></div>';
- if (m.Size && m.Speed) { x += addDetailItem("Capacity / Speed", format("{0}, {1}", m.Size, m.Speed), s); }
- else if (m.Size) { x += addDetailItem("Capacity", format("{0}", (m.Size)), s); }
- if (m.PartNumber) { x += addDetailItem("Part Number", EscapeHtml((m.Manufacturer && m.Manufacturer != '')?(m.Manufacturer + ', '):'') + EscapeHtml(m.PartNumber), s); }
- x += '</div>';
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Memory", html: x, img: 'ram'}); }
- }
- }
- // Storage
- if (hardware.identifiers && ident.storage_devices) {
- var x = '';
- // Sort Storage
- ident.storage_devices.sort(function (a, b) { if (a.Caption > b.Caption) return 1; if (a.Caption < b.Caption) return -1; return 0; });
- x += '<table style=width:100%>';
- for (var i in ident.storage_devices) {
- var m = ident.storage_devices[i];
- if (m.Size) {
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.Caption) + '</b></div>';
- if (m.Model && (m.Model != m.Caption)) { x += addDetailItem("Model", EscapeHtml(m.Model), s); }
- if (m.Size) {
- if ((typeof m.Size == 'string') && (parseInt(m.Size) == m.Size)) { m.Size = parseInt(m.Size); }
- if (typeof m.Size == 'number') { x += addDetailItem("Capacity", format("{0} Mb", Math.floor(m.Size / 1024 / 1024)), s); }
- if (typeof m.Size == 'string') { x += addDetailItem("Capacity", EscapeHtml(m.Size), s); }
- }
- if(hardware.windows && hardware.windows.drives && m.Model){
- const foundObject = hardware.windows.drives.find(obj => obj['Model'] === m.Model);
- if(foundObject) x += addDetailItem("Status", EscapeHtml(foundObject.Status), s);
- }
- x += '</div>';
- }
- }
- x += '</table>';
- if (x != '') { sections.push({ name: "Storage", html: x, img: 'storage' }); }
- }
- // Volumes and Bitlocker
- if (hardware.windows && hardware.windows.volumes) {
- var x = '';
- for (var i in hardware.windows.volumes) {
- var m = hardware.windows.volumes[i];
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + i + ':' + (((m.name == null) || (m.name == '')) ? '' : (' - ' + EscapeHtml(m.name))) + '</b></div>';
- if (m.size) {
- var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- var j = parseInt(Math.floor(Math.log(Math.abs(m.size)) / Math.log(1024)), 10);
- var fsize = (j === 0 ? `${m.size} ${sizes[j]}` : `${(m.size / (1024 ** j)).toFixed(2)} ${sizes[j]}`);
- x += addDetailItem("Capacity", EscapeHtml(fsize), s);
- }
- if (m.sizeremaining) {
- var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- var j = parseInt(Math.floor(Math.log(Math.abs(m.sizeremaining)) / Math.log(1024)), 10);
- var fsize = (j === 0 ? `${m.sizeremaining} ${sizes[j]}` : `${(m.sizeremaining / (1024 ** j)).toFixed(2)} ${sizes[j]}`);
- x += addDetailItem("Capacity Remaining", EscapeHtml(fsize), s);
- }
- if (m.type) {
- var type = (m.removable == true ? "Removable" : (m.cdrom == true ? "CD-ROM" : ''));
- x += addDetailItem("File System", (type != '' ? (type + ' / ') : '') + (m.type == 'Unknown' ? "Unknown" : EscapeHtml(m.type)), s);
- }
- if (m.protectionStatus || m.volumeStatus) {
- var bitlockerState = [];
- if (m.protectionStatus) bitlockerState.push("Enabled");
- if (m.volumeStatus && m.volumeStatus == 'FullyDecrypted') bitlockerState.push("Fully Decrypted");
- if (m.volumeStatus && m.volumeStatus == 'EncryptionInProgress') bitlockerState.push("Encryption In Progress");
- if (m.volumeStatus && m.volumeStatus == 'FullyEncrypted') bitlockerState.push("Fully Encrypted");
- bitlockerState = bitlockerState.join(' - ');
- if (m.recoveryPassword) { bitlockerState += addKeyLink('', 'deviceDetailsShowBitlockerInfo(\"' + encodeURIComponentEx(i) + '\",\"' + encodeURIComponentEx(m.identifier) + '\",\"' + encodeURIComponentEx(m.recoveryPassword) + '\")'); }
- x += addDetailItem("BitLocker", bitlockerState, s);
- }
- x += '</div>';
- }
- if (x != '') { sections.push({ name: "Storage Volumes", html: '<table style=width:100%>' + x + '</table>', img: 'storage'}); }
- }
- // Linux Volumes
- if (hardware.linux && hardware.linux.volumes) {
- var x = '';
- for (var i in hardware.linux.volumes) {
- var m = hardware.linux.volumes[i];
- if(m.mount_point.startsWith('/var/lib/docker/overlay2')) continue;
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + m.mount_point + '</b></div>';
- if (m.size) {
- var sizes = ['KB', 'MB', 'GB', 'TB'];
- var j = parseInt(Math.floor(Math.log(Math.abs(m.size)) / Math.log(1024)), 10);
- var fsize = (j === 0 ? `${m.size} ${sizes[j]}` : `${(m.size / (1024 ** j)).toFixed(2)} ${sizes[j]}`);
- x += addDetailItem("Capacity", EscapeHtml(fsize), s);
- }
- if (m.available) {
- if (Math.abs(m.available) == 0) {
- var fsize = `0 KB`;
- } else {
- var sizes = ['KB', 'MB', 'GB', 'TB'];
- var j = parseInt(Math.floor(Math.log(Math.abs(m.available)) / Math.log(1024)), 10);
- var fsize = (j === 0 ? `${m.available} ${sizes[j]}` : `${(m.available / (1024 ** j)).toFixed(2)} ${sizes[j]}`);
- }
- x += addDetailItem("Capacity Remaining", EscapeHtml(fsize), s);
- }
- if (m.type) {
- var type = (m.removable == true ? "Removable" : (m.cdrom == true ? "CD-ROM" : ''));
- x += addDetailItem("File System", (type != '' ? (type + ' / ') : '') + (m.type == 'Unknown' ? "Unknown" : EscapeHtml(m.type)), s);
- }
- x += '</div>';
- }
- if (x != '') { sections.push({ name: "Storage Volumes", html: '<table style=width:100%>' + x + '</table>', img: 'storage'}); }
- }
- // MacOS Volumes
- if (hardware.darwin && hardware.darwin.volumes) {
- var x = '';
- for (var i in hardware.darwin.volumes) {
- var m = hardware.darwin.volumes[i];
- if(m.mount_point.startsWith('/var/lib/docker/overlay2')) continue;
- x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>';
- x += '<div style=margin-bottom:3px><b>' + m.mount_point + '</b></div>';
- if (m.size) {
- x += addDetailItem("Capacity", EscapeHtml(m.size), s);
- }
- if (m.available) {
- x += addDetailItem("Capacity Remaining", EscapeHtml(m.available), s);
- }
- if (m.type) {
- var type = (m.removable == true ? "Removable" : (m.cdrom == true ? "CD-ROM" : ''));
- x += addDetailItem("File System", (type != '' ? (type + ' / ') : '') + (m.type == 'Unknown' ? "Unknown" : EscapeHtml(m.type)), s);
- }
- x += '</div>';
- }
- if (x != '') { sections.push({ name: "Storage Volumes", html: '<table style=width:100%>' + x + '</table>', img: 'storage'}); }
- }
- // Render the sections
- var x = '';
- for (var i in sections) {
- if (sections[i].img == null) {
- x += '<div class=DevSt style=margin-bottom:3px;margin-left:4px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:4px>' + sections[i].html + '</div>';
- } else {
- x += '<table style=width:100%><tr>';
- x += '<td style=width:32px;vertical-align:top><img src=images/details/' + sections[i].img + '32.png srcset="images/details/' + sections[i].img + '64.png 2x" border=0 width=32 /></td>'; // height=12
- x += '<td><div class=DevSt style=margin-bottom:3px;margin-left:4px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:4px>' + sections[i].html + '</div></td>';
- x += '</tr></table>';
- }
- }
- if (x == '') {
- QH('p10detailshtml', "No information for this device.");
- } else {
- QH('p10detailshtml', x);
- }
- }
- function deviceDetailsShowBitlockerInfo(drive, identifier, password) {
- if (xxdialogMode) return false;
- var x = '<div><p>' + "Identifier" + '</p><p style=user-select:text;font-weight:bold>' + (identifier ? decodeURIComponent(identifier) : "Unknown") + '</p>';
- x += '<p>' + "Recovery Password" + '</p><p style=user-select:text;font-weight:bold>' + (password ? decodeURIComponent(password) : "Unknown") + '</p></div>';
- setDialogMode(2, decodeURIComponent(drive) + ': ' + "BitLocker Information", 1, null, x, '');
- }
- //
- // CONSOLE
- //
- /*
- function agentConsoleHandleKeys(e) {
- if ((e.ctrlKey) || (e.altKey)) { return true; }
- var processed = 0, box = Q('p15consoleText');
- if (e.key) {
- if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; }
- else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; }
- else if (e.keyCode == 27) { box.value = ''; processed = 1; }
- else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down
- var hindex = consoleHistory.indexOf(box.value);
- //console.log(hindex, consoleHistory);
- if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; }
- else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; }
- else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
- processed = 1;
- }
- else if (e.key.length === 1) {
- //box.value = ((box.value + e.key));
- insertTextAtCursor(box, e.key);
- processed = 1;
- }
- } else {
- if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
- }
- if (processed > 0) { return haltEvent(e); }
- }
- */
- // Insert text at the cursor location on the
- function insertTextAtCursor(ctrl, val) {
- if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; }
- else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
- var start = ctrl.selectionStart, end = ctrl.selectionEnd;
- ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length);
- ctrl.setSelectionRange(end + 1, end + 1);
- } else { ctrl.value += myValue; }
- }
- var consoleNode;
- var consoleServerText = '';
- function setupConsole() {
- // Setup the console
- var samenode = (consoleNode == currentNode);
- consoleNode = currentNode;
- var mesh = meshes[consoleNode.meshid];
- var rights = GetNodeRights(currentNode);
- if ((rights & 16) != 0) {
- if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
- if (samenode == false) {
- QH('p15agentConsoleText', consoleNode.consoleText);
- Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
- }
- var online = (((consoleNode.conn & 1) != 0) || ((consoleNode.conn & 16) != 0)) ? true : false;
- var onlineText = ((consoleNode.conn & 1) != 0) ? "Agent is online" : "Agent is offline"
- if ((consoleNode.conn & 16) != 0) { onlineText += ", MQTT is online" }
- QH('p15statetext', onlineText);
- QE('p15uploadCore', ((consoleNode.conn & 1) != 0));
- QV('p15outputselecttd', ((consoleNode.conn & 16) != 0) || ((currentNode.pmt == 1) && ((features2 & 2) != 0)));
- QV('p15outputselect2', ((consoleNode.conn & 16) != 0)); // MQTT channel
- QV('p15outputselect3', ((currentNode.pmt == 1) && ((features2 & 2) != 0))); // Push Notification channel
- var c = Q('p15outputselect').value;
- if (((consoleNode.conn & 16) == 0) && (c == 2)) { c = 1; Q('p15outputselect').value = 1; }
- if (((currentNode.pmt != 1) || ((features2 & 2) == 0)) && (c == 3)) { c = 1; Q('p15outputselect').value = 1; }
- var active = false;
- if (((consoleNode.conn & 1) != 0) && (c == 1)) { active = true; } // Agent
- if (((consoleNode.conn & 16) != 0) && (c == 2)) { active = true; } // MQTT
- if (((currentNode.pmt == 1) && ((features2 & 2) != 0)) && (c == 3)) { active = true; } // Push
- QE('p15consoleText', active);
- } else {
- QH('p15statetext', "Access Denied");
- QE('p15consoleText', false);
- QE('p15uploadCore', false);
- QV('p15outputselecttd', false);
- }
- QV('devListToolbarViewIcons3', ((consoleNode.conn & 1) != 0));
- }
- // Clear the console for this node
- function p15consoleClear() {
- QH('p15agentConsoleText', '');
- Q('id_p15consoleClear').blur();
- consoleNode.consoleText = '';
- }
- // Send a command to the agent
- var consoleHistory = [];
- function p15consoleSend(e) {
- if (e && e.keyCode != 13) return;
- var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(v) + '<br/></div>';
- if (((consoleNode.conn & 16) != 0) && (Q('p15outputselect').value == 2)) {
- // Send the command to MQTT
- t = '<div style=color:orange>' + "MQTT" + '> ' + EscapeHtml(v) + '<br/></div>';
- consoleNode.consoleText += t;
- meshserver.send({ action: 'sendmqttmsg', topic: 'console', nodeids: [consoleNode._id], msg: v });
- } else if ((consoleNode.pmt == 1) && (Q('p15outputselect').value == 3) && ((features2 & 2) != 0)) {
- // Send the command using push notification
- t = '<div style=color:violet>' + "PUSH" + '> ' + EscapeHtml(v) + '<br/></div>';
- consoleNode.consoleText += t;
- meshserver.send({ action: 'pushconsole', nodeid: consoleNode._id, console: v });
- } else if ((consoleNode.conn & 1) != 0) {
- // Send the command to the mesh agent
- consoleNode.consoleText += t;
- meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value: v });
- }
- Q('p15agentConsoleText').innerHTML += t;
- Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
- Q('p15consoleText').value = '';
- // Add command to history list
- if (v.length > 0) {
- // Move this command to the top if it already exists
- var j = consoleHistory.indexOf(v);
- if (j >= 0) { consoleHistory.splice(j, 1); }
- consoleHistory.unshift(v);
- consoleHistory.splice(10);
- }
- }
- // Handle Mesh Agent console data
- function p15consoleReceive(node, data, source) {
- if (node === 'serverconsole') {
- // Server console data
- data = '<div>' + EscapeHtml(data) + '</div>'
- consoleServerText += data;
- if (consoleNode == 'server') {
- Q('p15agentConsoleText').innerHTML += data;
- Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
- }
- } else {
- // Agent console data
- if (source == 'MQTT') { data = '<div style=color:red>' + "MQTT" + '> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + EscapeHtml(data) + '</div>' }
- if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
- if (consoleNode == node) {
- Q('p15agentConsoleText').innerHTML += data;
- Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
- }
- }
- }
- // Save console text to file
- function p15downloadConsoleText() {
- saveAs(new Blob([Q('p15agentConsoleText').innerText], { type: 'application/octet-stream' }), "console.txt");
- }
- // Called then user presses the "Change Core" button
- function p15uploadCore(e) {
- if (xxdialogMode) return;
- if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core
- else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core
- else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
- else {
- var htmlValue = '<select id=d3coreMode style=width:230px>' +
- '<option value=1>' + "Upload default server core" + '</option>' +
- '<option value=2>' + "Clear the core" + '</option>' +
- '<option value=3>' + "Upload a core file" + '</option>' +
- '<option value=4>' + "Soft disconnect agent" + '</option>' +
- '<option value=5>' + "Hard disconnect agent" + '</option>' +
- '<option value=6>' + "Upload recovery core" + '</option>' +
- '<option value=7>' + "Upload tiny core" + '</option>' +
- '<option value=8>' + "Restart agent service" + '</option>' +
- '<option value=9>' + "Force agent update" + '</option></select>';
- setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", htmlValue));
- }
- }
- function p15uploadCoreEx() {
- if (Q('d3coreMode').value == 1) {
- // Upload default core
- meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' });
- } else if (Q('d3coreMode').value == 2) {
- // Clear the core
- meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' });
- } else if (Q('d3coreMode').value == 3) {
- // Upload file as core
- p15uploadCore2();
- } else if (Q('d3coreMode').value == 4) {
- // Soft disconnect the mesh agent
- meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 1 });
- } else if (Q('d3coreMode').value == 5) {
- // Hard disconnect the mesh agent
- meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 2 });
- } else if (Q('d3coreMode').value == 6) {
- // Upload a recovery core
- meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'recovery' });
- } else if (Q('d3coreMode').value == 7) {
- // Upload a tiny core
- meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'tiny' });
- } else if (Q('d3coreMode').value == 8) {
- // Restart MeshAgent service
- meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value:'service restart' });
- } else if (Q('d3coreMode').value == 9) {
- // Update mesh agent
- meshserver.send({ action: 'updateAgents', nodeids: [consoleNode._id] });
- }
- }
- // Called then user opts to upload a file as core
- function p15uploadCore2() {
- if (xxdialogMode) return;
- Q('d3localmodeform').action = 'uploadmeshcorefile.ashx';
- Q('d3auth').value = authCookie;
- Q('d3attrib').value = currentNode._id;
- setDialogMode(4, "Upload Mesh Agent Core", 3, p15uploadCoreEx2);
- d3init();
- }
- function p15uploadCoreEx2() {
- var mode = Q('d3uploadMode').value;
- if (mode == 1) {
- // Upload local mesh agent core
- Q('d3submit').click();
- } else {
- // Upload server mesh agent code
- var files = d3getFileSel();
- if (files.length == 1) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'custom', path: d3filetreelocation.join('/') + '/' + files[0] }); }
- }
- }
- //
- // MY MESHS
- //
- var currentMesh;
- function p20updateMesh() {
- if (currentMesh == null) return;
- QH('p20meshName', EscapeHtml(currentMesh.name));
- var meshtype = format("Unknown #{0}", currentMesh.mtype);
- var meshrights = GetMeshRights(currentMesh);
- if (currentMesh.mtype == 1) meshtype = "Intel® AMT only, no agent";
- if (currentMesh.mtype == 2) meshtype = "Managed using a software agent";
- if (currentMesh.mtype == 3) { if (currentMesh.relayid == null) { meshtype = "Local devices, no agent"; } else { meshtype = "No agent devices relayed thru agent"; } }
- if (currentMesh.mtype == 4) { if (currentMesh.relayid == null) { meshtype = "IP-KVM device"; } else { meshtype = "IP-KVM device relayed thru agent"; } if (currentMesh.kvm.model == 1) { meshtype += ', ' + 'Raritan KX III'; } }
- var x = '';
- x += addHtmlValue("Name", addLinkConditional(EscapeHtml(currentMesh.name), 'p20editmesh(1)', (meshrights & 1) != 0));
- x += addHtmlValue("Description", addLinkConditional(((currentMesh.desc && currentMesh.desc != '') ? EscapeHtml(currentMesh.desc) : ('<i>' + "None" + '</i>')), 'p20editmesh(2)', (meshrights & 1) != 0));
- x += addHtmlValue("Type", meshtype);
- //x += addHtmlValue('Identifier', currentMesh._id.split('/')[2]);
- // Display the relay device if applicable
- if (((currentMesh.mtype == 3) || (currentMesh.mtype == 4)) && (currentMesh.relayid != null)) {
- var relayName = '<i>' + "Unknown" + '</i>';
- var relayNode = getNodeFromId(currentMesh.relayid);
- if (relayNode != null) { relayName = EscapeHtml(relayNode.name); }
- x += addHtmlValue("Relay Device", addLinkConditional(relayName, 'p20editmeshrelay()', (meshrights & 1) != 0));
- }
- // Display IP-KVM information if needed
- if (currentMesh.mtype == 4) {
- x += addHtmlValue("Hostname", currentMesh.kvm.host);
- x += addHtmlValue("Username", currentMesh.kvm.user);
- }
- x += '<br><input type=button value="' + "Notes" + '" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
- x += '<br style=clear:both><br>';
- var currentMeshLinks = currentMesh.links[userinfo._id];
- if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<div style=margin-bottom:6px;float:left;margin-right:10px><a onclick=p20showAddMeshUserDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User" + '</a></div>'; }
- if (navigator.userAgent.toLowerCase().indexOf('android') >= 0) {
- x += '<div style=margin-bottom:6px;float:left;margin-right:10px><a onclick=p20installAndroidDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Install on this device" + '</a></div>';
- }
- /*
- if ((meshrights & 4) != 0) {
- if (currentMesh.mtype == 1) {
- x += '<a onclick=addCiraDeviceToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-installmesh.png border=0 height=12 width=12> Install CIRA</a>';
- x += '<a onclick=addDeviceToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-installmesh.png border=0 height=12 width=12> Install local</a>';
- }
- if (currentMesh.mtype == 2) {
- x += '<a onclick=addAgentToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> Install</a>';
- }
- }
- */
- /*
- function getMeshActions(mesh, meshrights) {
- if ((meshrights & 4) == 0) return '';
- var r = '';
- if (mesh.mtype == 1) {
- r += ' <a style=cursor:pointer;font-size:10px onclick=addCiraDeviceToMesh("' + mesh._id + '")>Add CIRA</a>';
- r += ' <a style=cursor:pointer;font-size:10px onclick=addDeviceToMesh("' + mesh._id + '")>Add Local</a>';
- }
- if (mesh.mtype == 2) {
- r += ' <a style=cursor:pointer;font-size:10px onclick=addAgentToMesh("' + mesh._id + '")>Add Agent</a>';
- }
- return r;
- }
- */
- x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "User Authorizations" + '</th></tr>';
- // Sort the users for this mesh
- var count = 1, sortedusers = [];
- for (var i in currentMesh.links) {
- var uname = i.split('/')[2];
- if (currentMesh.links[i].name) { uname = currentMesh.links[i].name; }
- if (i == userinfo._id) { uname = userinfo.name; }
- if ((usergroups != null) && (usergroups[i] != null)) { uname = usergroups[i].name; }
- sortedusers.push({ id: i, name: uname, rights: currentMesh.links[i].rights });
- }
- sortedusers.sort(function (a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; });
- // Display all users for this mesh
- for (var i in sortedusers) {
- var trash = '', rights = "Partial Rights", r = sortedusers[i].rights, icon = 2;
- if (r == 0xFFFFFFFF) rights = "Full Administrator"; else if (r == 0) rights = "No Rights";
- if ((i != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) { trash = '<a onclick=p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '") style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
- if (sortedusers[i].id.startsWith('ugrp/')) { icon = 4; }
- x += '<tr onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") style=height:32px;cursor:pointer' + (((count % 2) == 0) ? ';background-color:#DDD' : '') + '><td>';
- x += '<div style=float:right>' + trash + '</div><div style=float:right;padding-right:4px>' + rights + '</div><div class=m' + icon + '></div><div> ' + EscapeHtml(decodeURIComponent(sortedusers[i].name)) + '<div></div></div>';
- x += '</td></tr>';
- ++count;
- }
- x += '</tbody></table>';
- // If we are full administrator on this mesh, allow deletion of the mesh
- if (meshrights == 0xFFFFFFFF) { x += '<div style=font-size:small;text-align:right;margin-top:6px><span><a onclick=p20showDeleteMeshDialog() style=cursor:pointer>' + "Delete Group" + '</a></span></div>'; }
- QH('p20info', x);
- }
- function p20showDeleteMeshDialog() {
- if (xxdialogMode) return false;
- var x = format("Are you sure you want to delete group {0}? Deleting the device group will also delete all information about devices within this group.", EscapeHtml(currentMesh.name)) + '<br /><br />';
- x += '<label><input id=p20check type=checkbox onchange=p20validateDeleteMeshDialog() />' + "Confirm" + '</label>';
- setDialogMode(2, "Delete Group", 3, p20showDeleteMeshDialogEx, x);
- p20validateDeleteMeshDialog();
- return false;
- }
- function p20validateDeleteMeshDialog() {
- QE('idx_dlgOkButton', Q('p20check').checked);
- }
- function p20showDeleteMeshDialogEx(buttons, tag) {
- meshserver.send({ action: 'deletemesh', meshid: currentMesh._id, meshname: currentMesh.name });
- }
- function p20editmeshrelay() {
- if (xxdialogMode) return;
- // Look for all relay devices
- var relayDevices = [];
- if ((features & 2) == 0) { for (var i in nodes) { var node = nodes[i]; if ((node.mtype == 2) && (node.agent != null) && (GetNodeRights(node) == 0xFFFFFFFF)) { relayDevices.push(node); } } }
- relayDevices.sort(nameSort);
- if (relayDevices.length == 0) {
- // Relay relay devices available
- setDialogMode(2, "Edit Device Group", 1, null, "No relay devices available.");
- } else {
- var relayDevices2 = [];
- for (var i in relayDevices) { relayDevices2.push('<option value="' + (relayDevices[i]._id + '"' + ((currentMesh.relayid == relayDevices[i]._id) ? ' selected' : '')) + '>' + EscapeHtml(relayDevices[i].name) + '</option>'); }
- var x = addHtmlValue("Relay Device", '<div style=width:170px><select id=d2devrelay style=width:100%>' + relayDevices2.join('') + '</select></div>');
- setDialogMode(2, "Edit Device Group", 3, p20editmeshrelayEx, x);
- }
- }
- function p20editmeshrelayEx() {
- meshserver.send({ action: 'editmesh', meshid: currentMesh._id, relayid: Q('d2devrelay').value });
- }
- function p20editmesh(focus) {
- if (xxdialogMode) return;
- var x = addHtmlValue("Name", '<input id=dp20meshname style=width:170px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />');
- x += addHtmlValue("Description", '<input id=dp20meshdesc style=width:170px maxlength=1024 onkeyup=p20editmeshValidate() />');
- setDialogMode(2, "Edit Device Group", 3, p20editmeshEx, x);
- Q('dp20meshname').value = currentMesh.name;
- if (currentMesh.desc) Q('dp20meshdesc').value = currentMesh.desc;
- p20editmeshValidate();
- if (focus == 2) { Q('dp20meshdesc').focus(); } else { Q('dp20meshname').focus(); }
- }
- function p20editmeshEx() {
- meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value });
- }
- function p20editmeshValidate() {
- QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0);
- }
- function p20installAndroidDialog() {
- if (xxdialogMode) return;
- var x = '<div style=text-align:center><p>' + "Install the MeshCentral Agent on your Android device. Once installed, click the pairing link to connect your device to this server." + '</p>';
- x += '<p><a rel=\"noreferrer noopener\" target=_blank href=\"https://play.google.com/store/apps/details?id=com.meshcentral.agent2\"><img style=cursor:pointer src=\"images/google-play-140.png\" width=140 srcset=\"images/google-play-280.png 2x\" /></a></p>';
- x += '<p><a rel=\"noreferrer noopener\" target=_blank href=\"https://www.amazon.co.uk/gp/product/B097Z4Q7SK/\"><img style=cursor:pointer src=\"images/amazon-appstore-140.png\" width=140 srcset=\"images/amazon-appstore-280.png 2x\" /></a></p>';
- x += '<p><a rel=\"noreferrer noopener\" target=_blank href="meshagents?id=14' + (urlargs.key?('&key=' + urlargs.key):'') + '" title="' + "APK version of the MeshAgent" + '">' + "Android APK" + '</a></p>';
- x += '<p><a href="' + serverinfo.magenturl + ',' + serverinfo.agentCertHash + ',' + currentMesh._id.split('/')[2] + '"><b>' + "Device Pairing Link" + '</b></a></p></div>';
- setDialogMode(2, "Android Installation", 1, null, x);
- }
- function p20showAddMeshUserDialog() {
- if (xxdialogMode) return;
- var x = addHtmlValue('User ID', '<input id=dp20username style=width:170px maxlength=256 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />');
- x += '<div style="border:2px groove gray;background-color:white;max-height:120px;overflow-y:scroll">';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>' + "Full Administrator" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>' + "Edit Device Group" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20manageusers>' + "Manage Device Group Users" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20managecomputers>' + "Manage Device Group Computers" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotecontrol>' + "Remote Control" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remoteview style=margin-left:12px>' + "Remote View Only" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotelimitedinput style=margin-left:12px>' + "Limited Input Only" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noterminal style=margin-left:12px>' + "No Terminal Access" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20nofiles style=margin-left:12px>' + "No File Access" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noamt style=margin-left:12px>' + "No Intel® AMT" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>' + "Mesh Agent Console" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>' + "Server Files" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>' + "Wake Devices" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>' + "Edit Device Notes" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20limitevents>' + "Show Only Own Events" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20chatnotify>' + "Chat & Notify" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20uninstall>' + "Uninstall Agent" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20commands>' + "Remote Commands" + '</label><br>';
- x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20resetoff>' + "Reset / Power Off" + '</label><br>';
- x += '</div>';
- setDialogMode(2, "Add User to Device Group", 3, p20showAddMeshUserDialogEx, x);
- p20validateAddMeshUserDialog();
- Q('dp20username').focus();
- }
- function p20validateAddMeshUserDialog() {
- var meshrights = GetMeshRights(currentMesh);
- var nc = !Q('p20fulladmin').checked;
- QE('p20fulladmin', meshrights == 0xFFFFFFFF);
- QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF));
- QE('p20manageusers', nc);
- QE('p20managecomputers', nc);
- QE('p20remotecontrol', nc);
- QE('p20meshagentconsole', nc);
- QE('p20meshserverfiles', nc);
- QE('p20wakedevices', nc);
- QE('p20editnotes', nc);
- QE('p20limitevents', nc);
- QE('p20remoteview', nc && Q('p20remotecontrol').checked);
- QE('p20remotelimitedinput', nc && Q('p20remotecontrol').checked && !Q('p20remoteview').checked);
- QE('p20noterminal', nc && Q('p20remotecontrol').checked);
- QE('p20nofiles', nc && Q('p20remotecontrol').checked);
- QE('p20noamt', nc && Q('p20remotecontrol').checked);
- QE('p20chatnotify', nc);
- QE('p20uninstall', nc);
- QE('p20commands', nc);
- QE('p20resetoff', nc);
- }
- function p20showAddMeshUserDialogEx() {
- var meshadmin = 0;
- if (Q('p20fulladmin').checked == true) { meshadmin = 0xFFFFFFFF; } else {
- if (Q('p20editmesh').checked == true) meshadmin += 1;
- if (Q('p20manageusers').checked == true) meshadmin += 2;
- if (Q('p20managecomputers').checked == true) meshadmin += 4;
- if (Q('p20remotecontrol').checked == true) meshadmin += 8;
- if (Q('p20meshagentconsole').checked == true) meshadmin += 16;
- if (Q('p20meshserverfiles').checked == true) meshadmin += 32;
- if (Q('p20wakedevices').checked == true) meshadmin += 64;
- if (Q('p20editnotes').checked == true) meshadmin += 128;
- if (Q('p20remoteview').checked == true) meshadmin += 256;
- if (Q('p20noterminal').checked == true) meshadmin += 512;
- if (Q('p20nofiles').checked == true) meshadmin += 1024;
- if (Q('p20noamt').checked == true) meshadmin += 2048;
- if (Q('p20remotelimitedinput').checked == true) meshadmin += 4096;
- if (Q('p20limitevents').checked == true) meshadmin += 8192;
- if (Q('p20chatnotify').checked == true) meshadmin += 16384;
- if (Q('p20uninstall').checked == true) meshadmin += 32768;
- if (Q('p20commands').checked == true) meshadmin += 131072;
- if (Q('p20resetoff').checked == true) meshadmin += 262144;
- }
- var users = Q('dp20username').value.split(','), users2 = [];
- for (var i in users) { users2.push(users[i].trim()); }
- meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, usernames: users2, meshadmin: meshadmin });
- }
- function p20viewuser(userid) {
- if (xxdialogMode) return;
- userid = decodeURIComponent(userid);
- var r = [], cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid);
- if (meshrights == 0xFFFFFFFF) r.push("Full Administrator"); else {
- if ((meshrights & 1) != 0) r.push("Edit Device Group");
- if ((meshrights & 2) != 0) r.push("Manage Device Group Users");
- if ((meshrights & 4) != 0) r.push("Manage Device Group Computers");
- if ((meshrights & 8) != 0) r.push("Remote Control");
- if ((meshrights & 16) != 0) r.push("Agent Console");
- if ((meshrights & 32) != 0) r.push("Server Files");
- if ((meshrights & 64) != 0) r.push("Wake Devices");
- if ((meshrights & 128) != 0) r.push("Edit Notes");
- if ((meshrights & 256) != 0) r.push("Remote View Only");
- if ((meshrights & 512) != 0) r.push("No Terminal");
- if ((meshrights & 1024) != 0) r.push("No Files");
- if ((meshrights & 2048) != 0) r.push("No Intel® AMT");
- if (((meshrights & 8) != 0) && ((meshrights & 4096) != 0) && ((meshrights & 256) == 0)) r.push("Limited Input");
- if ((meshrights & 8192) != 0) r.push("Self Events Only");
- if ((meshrights & 16384) != 0) r.push("Chat & Notify");
- if ((meshrights & 32768) != 0) r.push("Uninstall");
- if ((meshrights & 131072) != 0) r.push("Commands");
- if ((meshrights & 262144) != 0) r.push("Reset/Off");
- }
- if (r.length == 0) { r.push("No Rights"); }
- var buttons = 1, uname = userid.split('/')[2];
- if (currentMesh.links[userid].name) { uname = currentMesh.links[userid].name; }
- var x = addHtmlValue("User Name", EscapeHtml(uname));
- if (uname != userid.split('/')[2]) { x += addHtmlValue("User ID", EscapeHtml(userid.split('/')[2])); }
- x += addHtmlValue("Permissions", r.join(", "));
- if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4;
- setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, userid);
- }
- function p20viewuserEx(button, userid) {
- if (button != 2) return;
- var uname = userid.split('/')[2];
- if (users && users[userid]) { uname = users[userid].name; }
- if (usergroups && usergroups[userid]) { uname = usergroups[userid].name; }
- if (userinfo._id == userid) { uname = userinfo.name; }
- setDialogMode(2, "Remote Mesh User", 3, p20viewuserEx2, format("Confirm removal of user {0}?", uname), userid);
- }
- function p20deleteUser(e, userid) { haltEvent(e); p20viewuserEx(2, decodeURIComponent(userid)); }
- function p20viewuserEx2(button, userid) { meshserver.send({ action: 'removemeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userid: userid }); }
- //
- // NOTIFICATIONS
- //
- var notifications = [];
- // Toggle showing notifications
- function clickNotificationIcon(show) {
- //addNotification({ icon:0, text:'test' });
- if (show == true) { QV('notifiyBox', true); } else if (show == false) { QV('notifiyBox', false); } else { QV('notifiyBox', QS('notifiyBox')['display'] == 'none'); }
- drawNotifications();
- }
- // Set the notification count on the upper right oft he screen
- function setNotificationCount(c) {
- if (parseInt(Q('notificationCount').innerHTML) == c) return; // If the count did not change, exit now.
- QH('notificationCount2', c);
- QV('notificationCount', c > 0);
- }
- // Refresh the notification box
- function drawNotifications() {
- var notifySettings = getstore('notifications', 0);
- var r = '';
- if (notifications.length == 0) {
- r = '<div style=margin:5px>' + "There are currently no notifications" + '</div>';
- } else {
- for (var i in notifications) {
- var n = notifications[i], t = '', d = new Date(n.time), icon = 0;
- if (n.title != null) { t = '<b>' + EscapeHtml(n.title) + '</b>: ' }
- if (n.nodeid != null) {
- var node = getNodeFromId(n.nodeid);
- if (node != null) {
- icon = node.icon;
- if (notifySettings & 16) { t = '<b>' + EscapeHtml(meshes[node.meshid].name) + ' / ' + EscapeHtml(node.name) + '</b>: '; } else { t = '<b>' + EscapeHtml(node.name) + '</b>: '; } // Display with or without group name
- }
- }
- r += '<div title="' + format("Occured at {0}", printDateTime(d)) + '" id="notifyx' + n.id + '" class=notification style="cursor:pointer;border-top:1px solid ' + ((r == '') ? 'transparent' : 'orange') + '">';
- if (icon) { r += '<div class=j' + icon + ' onclick="notificationSelected(' + n.id + ')" style=margin:5px;float:left></div>'; }
- r += '<div onclick="notificationDelete(' + n.id + ')" class=unselectable title="' + "Clear this notification" + '" style=margin:5px;float:right;color:orange><b>X</b></div><div onclick="notificationSelected(' + n.id + ')" style=margin:5px>' + t + EscapeHtml(n.text) + '</div><div style=margin-left:5px;margin-bottom:5px;color:gray;font-size:10px>' + printDateTime(d) + '</div></div>';
- }
- }
- var deleteall = '';
- if (notifications.length > 1) { deleteall = '<div id="notifyRemoveAll" onclick="deleteAllNotifications()" style="cursor:pointer;border-top:1px solid orange;margin:5px;color:orange;text-align:right;padding-right:3px">' + "Clear all" + '</div>'; }
- QH('notifiyBox', '<div class=customScroll style="max-height:170px;overflow-y:auto;margin:5px">' + r + deleteall + '</div>');
- }
- // A notification was selected
- function notificationSelected(id, del) {
- var j = -1;
- for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
- if (j != -1) {
- notificationSelectedEx(notifications[j], id);
- if (del && notifications[j]) {
- if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
- notificationDelete(id);
- }
- }
- }
- function notificationSelectedEx(n, id) {
- if (n.nodeid != null) {
- if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
- else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
- else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
- else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
- else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
- else gotoDevice(n.nodeid, 10); // General
- } else {
- if ((n.tag == 'backupcodes') && !xxdialogMode) { account_manageOtp(0); notificationDelete(id); } // 2FA backup codes
- else if ((n.tag != null) && n.tag.startsWith('meshmessenger/')) {
- safeNewWindow('/messenger?id=' + n.tag + '&title=' + encodeURIComponentEx(n.username), n.tag.split('/')[2]);
- notificationDelete(id);
- } else if (n.url != null) {
- safeNewWindow(n.url);
- notificationDelete(id);
- }
- }
- }
- // Remove one notification
- function notificationDelete(id) {
- var j = -1, e = Q('notifyx' + id);
- if (e != null) {
- for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
- if (j != -1) {
- meshserver.send({ action: 'intersession', subaction: 'removeNotify', id: id }); // Remove the notification in other sessions of the same user.
- if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
- notifications.splice(j, 1);
- e.parentNode.removeChild(e);
- setNotificationCount(notifications.length);
- if (notifications.length == 0) { QV('notifiyBox', false); }
- if (notifications.length == 1) { QV('notifyRemoveAll', false); }
- if ((notifications.length > 0) && (j == 0)) {
- var n = notifications[0];
- QS('notifyx' + n.id)['border-top'] = '1px solid transparent';
- }
- }
- }
- }
- // Add a new notification and play the notification sound
- function addNotification(n) {
- // Perform message translation
- var translatedTitles = [
- null,
- "New Account", // 1
- "Server Limit",
- "Security Warning",
- "Account Settings",
- "Device Group",
- "Invite Codes"
- ];
- var translatedMessages = [
- null,
- "Permission denied", // 1
- "Invalid username",
- "Invalid password",
- "Invalid email",
- "Invalid domain",
- "Invalid site permissions",
- "User already exists",
- "Unable to add user in this mode",
- "Validation exception",
- "Account limit reached.", // 10
- "Chat Request, Click here to accept.",
- "There has been {0} failed login attempts on this account since the last login.",
- "Failed to change email address, another account already using: {0}.",
- "Email sent.",
- "User {0} not found.",
- "Users {0} not found.",
- "Error, unable to change to previously used password.",
- "Error, unable to change to commonly used password.",
- "Error, password not changed.",
- "Password changed.", // 20
- "Current password not correct.",
- "Error, invite code \"{0}\" already in use.",
- "SMS gateway not enabled",
- "No user management rights",
- "Invalid SMS message",
- "No phone number for this user",
- "SMS succesfuly sent.",
- "SMS error",
- "SMS error: {0}",
- "Email domain \"{0}\" is not allowed. Only ({1}) are allowed" // 30
- ];
- if (typeof n.titleid == 'number') { try { n.title = translatedTitles[n.titleid]; } catch (ex) { } }
- if (typeof n.msgid == 'number') { try { n.text = translatedMessages[n.msgid]; if (Array.isArray(n.args)) { n.text = format(n.text, n.args[0], n.args[1], n.args[2], n.args[3], n.args[4], n.args[5]); } } catch (ex) { } }
- // Show notification within the web page.
- if (n.time == null) { n.time = Date.now(); }
- if (n.id == null) { n.id = Math.random(); }
- notifications.unshift(n);
- setNotificationCount(notifications.length);
- clickNotificationIcon(true);
- var notifySettings = getstore('notifications', 0);
- if (notifySettings & 1) { Q('chimes').play(); }
- // If web notifications are granted, use it.
- var notification = null;
- if (Notification && (Notification.permission == 'granted')) {
- var text = n.text.split('®').join('').split('<b>').join('').split('</b>').join('').split('<br />').join('\r\n'); // Clean up any HTML codes
- if (n.nodeid) {
- var node = getNodeFromId(n.nodeid);
- if (node) {
- if (notifySettings & 16) { // Notify with group name
- notification = new Notification(decodeURIComponent('{{{extitle}}}') + ' - ' + meshes[node.meshid].name + ' - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' });
- } else {
- notification = new Notification(decodeURIComponent('{{{extitle}}}') + ' - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' });
- }
- }
- } else {
- if (n.icon == null) { n.icon = 0; }
- var title = n.title;
- if (title == null) { title = ''; } else { title = ' - ' + n.title; }
- notification = new Notification(decodeURIComponent('{{{extitle}}}') + title, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + n.icon + '.png' });
- }
- notification.id = n.id;
- notification.xtag = n.tag;
- notification.url = n.url;
- notification.nodeid = n.nodeid;
- notification.username = n.username;
- notification.onclick = function (e) { notificationSelected(e.target.id, true); }
- n.notification = notification;
- }
- // If the notification has a max time, setup the timer here.
- if ((typeof n.maxtime == 'number') && (n.maxtime > 0)) { var trigger = function notifyRemoveTrigger() { notificationDelete(notifyRemoveTrigger.xid); }; trigger.xid = n.id; setTimeout(trigger, n.maxtime * 1000); }
- }
- // Remove all notifications
- function deleteAllNotifications() {
- notifications = [];
- setNotificationCount(0);
- drawNotifications();
- QV('notifiyBox', false);
- }
- //
- // PANELS
- //
- var xxcurrentView = -1;
- function go(x) {
- setSessionActivity();
- if (xxdialogMode || xxcurrentView == x) return;
- updateFooterMenu();
- setDialogMode(0);
- // Edit this line when adding a new screen
- for (var i = 0; i < 32; i++) { QV('p' + i, i == x); }
- xxcurrentView = x;
- updateCurrentUrl();
- }
- // Change the URL
- function updateCurrentUrl() {
- if (((features & 0x10000000) == 0) && (xxcurrentView > 0)) {
- var urlviewmode = '';
- if ((xxcurrentView >= 10) && (xxcurrentView <= 19)) { // Device Link
- if (currentNode != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotonode=' + currentNode._id.split('/')[2] + ((currentDevicePanel > 0)?('&panel=' + currentDevicePanel):''); }
- } else if ((xxcurrentView >= 20) && (xxcurrentView <= 29)) { // Device Group Link
- if (currentMesh != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotomesh=' + currentMesh._id.split('/')[2]; }
- } else if (xxcurrentView > 1) { urlviewmode = '?viewmode=' + xxcurrentView; }
- for (var i in urlargs) { urlviewmode += (((urlviewmode == '') ? '?' : '&') + i + '=' + urlargs[i]); }
- try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { }
- }
- }
- //
- // POPUP DIALOG
- //
- // undefined = Hidden, 1 = Generic Message
- var xxdialogMode;
- var xxdialogFunc;
- var xxdialogButtons;
- var xxdialogTag;
- // Display a dialog box
- // Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only)
- function setDialogMode(x, y, b, f, c, tag) {
- setSessionActivity();
- xxdialogMode = x;
- xxdialogFunc = f;
- xxdialogButtons = b;
- xxdialogTag = tag;
- QE('idx_dlgOkButton', true);
- QV('idx_dlgOkButton', b & 1);
- QV('idx_dlgCancelButton', b & 2);
- QV('id_dialogclose', (b & 2) || (b & 8));
- QV('idx_dlgDeleteButton', b & 4);
- QV('idx_dlgButtonBar', b & 7);
- if (y) QH('id_dialogtitle', y);
- for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
- QV('dialog', x);
- if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
- }
- function dialogclose(x) {
- setSessionActivity();
- var f = xxdialogFunc;
- var b = xxdialogButtons;
- var t = xxdialogTag;
- setDialogMode();
- if (((b & 8) || x) && f) f(x, t);
- }
- //
- // Access Control Functions
- // These must match server
- //
- // Remove user rights
- function removeUserRights(rights, userid) {
- if ((userid != userinfo._id) || (userinfo.removeRights == null)) return rights;
- var add = 0, substract = 0;
- if ((userinfo.removeRights & 0x00000008) != 0) { substract += 0x00000008; } // No Remote Control
- if ((userinfo.removeRights & 0x00010000) != 0) { add += 0x00010000; } // No Desktop
- if ((userinfo.removeRights & 0x00000100) != 0) { add += 0x00000100; } // Desktop View Only
- if ((userinfo.removeRights & 0x00000200) != 0) { add += 0x00000200; } // No Terminal
- if ((userinfo.removeRights & 0x00000400) != 0) { add += 0x00000400; } // No Files
- if ((userinfo.removeRights & 0x00000010) != 0) { substract += 0x00000010; } // No Console
- if ((userinfo.removeRights & 0x00008000) != 0) { substract += 0x00008000; } // No Uninstall
- if ((userinfo.removeRights & 0x00020000) != 0) { substract += 0x00020000; } // No Remote Command
- if ((userinfo.removeRights & 0x00000040) != 0) { substract += 0x00000040; } // No Wake
- if ((userinfo.removeRights & 0x00040000) != 0) { substract += 0x00040000; } // No Reset/Off
- if (rights != 0xFFFFFFFF) {
- // If not administrator, add and subsctract restrictions
- rights |= add;
- rights &= (0xFFFFFFFF - substract);
- } else {
- // If administrator for a device group, start with permissions and add and subsctract restrictions
- rights = 1 + 2 + 4 + 8 + 32 + 64 + 128 + 16384 + 32768 + 131072 + 262144 + 524288 + 1048576;
- rights |= add;
- rights &= (0xFFFFFFFF - substract);
- }
- return rights;
- }
- // Get the right of a user on a given device group
- function GetMeshRights(mesh, userid) {
- if (mesh == null) { return 0; }
- if (userid == null) { userid = userinfo._id; }
- if (typeof mesh == 'string') { mesh = meshes[mesh] }
- if ((mesh == null) || (mesh.links == null)) { return 0; }
- // Check if super user
- if (serverinfo.manageAllDeviceGroups && (userid == userinfo._id)) return removeUserRights(0xFFFFFFFF, userid);
- // Check device group link permission
- var rights = 0, r = mesh.links[userid];
- if (r != null) {
- if (r.rights == 0xFFFFFFFF) { return removeUserRights(0xFFFFFFFF, userid); } // User has full rights thru a device group link, stop here.
- rights = r.rights;
- }
- // Check permissions thru user groups
- var user = null;
- if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
- if (user != null) {
- for (var i in user.links) {
- if (i.startsWith('ugrp/')) {
- r = mesh.links[i];
- if (r != null) {
- if (r.rights == 0xFFFFFFFF) { return removeUserRights(0xFFFFFFFF, userid); } // User has full rights thru a user group, stop here.
- rights |= r.rights; // TODO: Deal with reverse permissions
- }
- }
- }
- }
- return removeUserRights(rights, userid);
- }
- // Returns true if the user can view the given device group
- function IsMeshViewable(mesh, userid) {
- if (mesh == null) { return false; }
- if (userid == null) { userid = userinfo._id; }
- if (typeof mesh == 'string') { mesh = meshes[mesh] }
- if ((mesh == null) || (mesh.links == null)) { return false; }
- if (mesh.links[userid] != null) { return true; } // User has visilibity thru a direct link
- // Check if user user
- if (serverinfo.manageAllDeviceGroups && (userid == userinfo._id)) return true;
- // Check permissions thru user groups
- var user = null;
- if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
- if (user != null) {
- for (var i in user.links) {
- if ((i.startsWith('ugrp/')) && (mesh.links[i] != null)) { return true; } // User has visilibity thru a user group
- }
- }
- return false;
- }
- // Return the user rights for a given node
- function GetNodeRights(node, userid) {
- if (node == null) { return 0; }
- if (userid == null) { userid = userinfo._id; }
- if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } }
- var r = GetMeshRights(node.meshid, userid);
- if (r == 0xFFFFFFFF) return removeUserRights(r, userid);
- // Check direct device rights using device data
- if ((node.links != null) && (node.links[userid] != null)) { r |= node.links[userid].rights; } // TODO: Deal with reverse permissions
- // Check direct device rights thru user groups
- if ((node.links != null) && (userinfo.links != null)) {
- for (var i in node.links) {
- if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { r |= node.links[i].rights; }
- }
- }
- // Check direct device rights using user data
- /*
- var user = null;
- if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } }
- if ((user != null) && (user.links != null)) {
- var r2 = user.links[node._id];
- if (r2 != null) {
- if (r2.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // User has full rights thru a device link, stop here.
- r |= r2.rights; // TODO: Deal with reverse permissions
- }
- }
- */
- return removeUserRights(r, userid);
- }
- // Return true if the device is visible to the user
- function IsNodeViewable(node, userid) {
- if (node == null) { return false; }
- if (userid == null) { userid = userinfo._id; }
- if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return false; } }
- if (IsMeshViewable(node.meshid, userid)) return true;
- // Check direct device visibility using device data
- if ((node.links != null) && (node.links[userid] != null)) { return true; }
- // Check direct device visibility thru user groups
- if ((node.links != null) && (userinfo.links != null)) {
- for (var i in node.links) { if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { return true; } }
- }
- return false;
- }
- //
- // Generic Methods
- //
- function nameSort(a, b) { var aa = a.name.toLowerCase(), bb = b.name.toLowerCase(); return sortCollator.compare(aa, bb); }
- function getNodeAmtVersion(node) { if ((node == null) || (node.intelamt == null) || (typeof node.intelamt.ver != 'string')) return 0; var verSplit = node.intelamt.ver.split('.'); if (verSplit.length < 2) return 0; return parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100); }
- function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } }
- function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
- function center() { if (xtermfit) xtermfit.fit(); onDevicesScroll(); QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); if (currentNode != null) { drawDeviceTimeline(); } }
- function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
- function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); }
- function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; }
- function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
- function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } }
- function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); }
- function reload() { window.location.href = window.location.href; }
- function getNodeFromId(id) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } return null; }
- function addHtmlValue(t, v) { return '<table><td style=width:120px>' + t + '<td><b>' + v + '</b></table>'; }
- function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; }
- function addHtmlValue4(t, v) { return '<table style=width:100%><td style=width:120px>' + t + '<td style=text-align:right><b>' + v + '</b></table>'; }
- function addLink(x, f) { return '<a style=cursor:pointer;text-decoration:none onclick=\'' + f + '\'>♦ ' + x + '</a>'; }
- function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
- function addKeyLink(x, f) { return '<span tabindex=0 style=cursor:pointer;text-decoration:none onclick=' + f + ' onkeypress="if (event.key==\'Enter\') { ' + f + ' } ">' + x + ' <img class=hoverButton src=images/key16.png></span>'; }
- function addKeyLinkConditional(x, t, c) { if (c) return '<span title=\'' + t + '\'>' + x + ' <img class=hoverButton src=images/key16.png></span>'; return x }
- function passwordcheck(p) { var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()]).{8,}/; return re.test(p); }
- function getFileSizeStr(size) { if (typeof size != 'number') { size = 0; } if (size == 1) return "1 byte"; return format('{0} bytes', size); }
- function focusTextBox(x) { setTimeout(function () { Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); }
- var isFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })();
- function printDate(d) { return d.toLocaleDateString(args.locale); }
- function printTime(d) { return d.toLocaleTimeString(args.locale); }
- function printDateTime(d) { return d.toLocaleString(args.locale); }
- function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); };
- function nobreak(x) { return x.split(' ').join(' '); }
- function getUserName(userid) {
- var useridsplit = userid.split('/'), userid2 = useridsplit[0] + '/' + useridsplit[1] + '/' + useridsplit[2], guestname = '';
- if ((useridsplit.length == 4) && (useridsplit[3].startsWith('guest:'))) { guestname = ' - ' + decode_utf8(atob(useridsplit[3].substring(6))); }
- if (users && users[userid2] != null) { if (users[userid2].realname != null) return (users[userid2].realname + guestname); else return (users[userid2].name + guestname); }
- if (currentNode && currentNode.links && currentNode.links[userid] && currentNode.links[userid].name != null) { return (currentNode.links[userid].name + guestname); }
- if (userid == userinfo._id) { return (userinfo.name + guestname); }
- if (nodes) { for (var a in nodes) { if (nodes[a].links) { for (var b in nodes[a].links) { if (nodes[a].links[b].name && b == userid) return (nodes[a].links[b].name + guestname); } } } }
- if (meshes) { for (var a in meshes) { if (meshes[a].links) { for (var b in meshes[a].links) { if (meshes[a].links[b].name && b == userid) return (meshes[a].links[b].name + guestname); } } } }
- return (useridsplit[2] + guestname);
- }
- function addDetailItem(title, value, state) { return '<table style=width:100%><td>' + nobreak(title) + '<td style=text-align:right>' + value + '</table>'; }
- function isPrivateIP(a) { return (a.startsWith('10.') || a.startsWith('172.16.') || a.startsWith('192.168.')); }
- function encodeURIComponentEx(txt) { return encodeURIComponent(txt).replace(/'/g, '%27'); };
- function safeNewWindow(url, target) { var newWindow = window.open(url, target, 'noopener,noreferrer'); if (newWindow) { newWindow.opener = null; } }
- function isWindowsNode(node) { if ((node.mtype != 2) || (node.agent == null) || (node.agent.id == null)) return false; return ([1,2,3,4,21,22,34,42,43].indexOf(node.agent.id) >= 0); }
- </script>
- </body>
- </html>
|