#RequireAdmin #Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=Google-plus.ico #AutoIt3Wrapper_Res_Comment=Web Links #AutoIt3Wrapper_Res_Description=Create desktop URL links from Userdata fields #AutoIt3Wrapper_Res_Fileversion=4.2.0.0 #AutoIt3Wrapper_Res_ProductName=WebLinks.exe #AutoIt3Wrapper_Res_CompanyName=Learning Tree International #AutoIt3Wrapper_Res_LegalCopyright=Learning Tree International #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include #include #include #include #include ;Developed by Robin Johnston 2020 ;V4.0.0 - Migrated to Cloudshare ;V4.0.2 - Fixed a glitch re detecting Mural ;V4.0.4 - Fixed the Individual URL detection and won't call real API unless it has to ;V4.1.0 - All good and added Powershell option ;V4.2.0 - Added no internet debug and removed all API access since no longer used Global $force = False Global $silent = False Global $LogFile = "WebLinks.log" Global $version = StringLeft(FileGetVersion(@ScriptFullPath),3) FileDelete(@ScriptDir & '\' & $LogFile) If StringInStr($cmdlineraw, "/f") Or Not @Compiled Then ;allow a forced update RegDelete("HKLM\Software\WebLinks","Class") RegDelete("HKLM\Software\WebLinks","MD5") $force = True EndIf Global $hLogFile = FileOpen(@ScriptDir & '\' & $LogFile, 1) WriteLog("", 1) If $force Then WriteLog("Forced restart with /f switch or not compiled, log cleared, registry emptied") Global $userdata="" ;Only run once If Not _Singleton(@ScriptName) Then WriteLog("Already running.") Exit EndIf If StringInStr($cmdlineraw, "/s") Then $silent = True WriteLog("Silent mode enabled via /s switch") Else ProgressOn("Web Links Info V" & $version,"Web Links deployment progress","Awaiting Info from Cloudshare metadata") EndIf ;Pull initial metadata from CS $backoff=1000+Random(1,1000,1) Do $data=InetRead("https://metadata.cloudshare.com/api/v3/unauthenticated/metadata") If Not @error Then $metadata=BinaryToString($data) WriteLog("CS Metadata: " & Json_Encode($metadata)) ;Note Metadata always starts with lowercase letter - should change this is backend! Local $oJSON = Json_Decode($metadata) ; Access the nested object Local $oCustom = Json_ObjGet($oJSON, "custom-properties") WriteLog("CS Custom-Properties: " & Json_Encode($oCustom)) ; Example: read a specific property $class=Json_ObjGet($oJSON,"class-id") Else ;Metadata could not be read, backoff ProgressSet(25,"Struggling to access the metadata. Waiting...") WriteLog("No metadata discovered, possible connection issues") Sleep($backoff) $backoff = $backoff * 2 SetError(1) ;it gets cleared so reset EndIf If $backoff > 64000 Then ;Have we been here before? If RegRead("HKLM\Software\WebLinks","Backoff") == "ONCE" Then ;Already done this once - so throw up error and quit RegDelete("HKLM\Software\WebLinks","Backoff") WriteLog("Quitting because no metadata discovered") MsgBox(0,"Web Links Error","Unable to pull Cloudshare metadata" & @CR & @CR & "Check URL below then reboot and try again" & @CR & "https://metadata.cloudshare.com/api/v3/unauthenticated/metadata/class-id") Exit Else RegWrite("HKLM\Software\WebLinks","Backoff", "REG_SZ", "ONCE") WriteLog("Rebooting to see if we can pickup metadata on subsequent boot") ProgressSet(25,"Unable to access metadata, check network/internet...") Sleep(5000) Shutdown(6) ;Reboot if taking too long EndIf EndIf Until Not @error ;hopefully got some metadata WriteLog("Cloudshare ClassId from metadata: " & $class) If Not StringInStr($class, "CO") Then WriteLog("No valid Cloudshare ClassId detected in metadata, Exiting...") Exit EndIf ;Compare classId to saved one in registry, if a match, exit (as in already run) If RegRead("HKLM\Software\WebLinks","Class") == $class And @Compiled Then ;It's a match so nothing more to do WriteLog("ClassId matched in registry, all seems OK, so exiting") Exit EndIf ;Need to decide if Instructor or Student VM Global $Instructor = False If StringInStr(Json_ObjGet($oJSON, "env-name"),"instructor") Then $instructor = True EndIf WriteLog("Instructor VM: " & $Instructor) ;Little extra bit to support Powershell commands If Not $silent Then ProgressSet(10,"Looking for a Powershell command") Sleep(1000) WriteLog("Looking for Powershell command - processing now...") ;Check if Powershell field defined.. If $oCustom.Exists("powershell") Then ;We have a Powershell command, lets run it - everything else gets ditched WriteLog("Possible Powershell command, checking") $powerShell = Json_ObjGet($oCustom, "powershell") If $powerShell="" Or $powerShell=Null Or @error Then WriteLog("No valid Powershell script found, skipping execution") Else ProgressSet(25,"Discovered a Powershell command, executing now") ConsoleWrite("Found Powershell: " & $powerShell & @CRLF) ; Run a PowerShell one-liner from AutoIt If @Compiled Then Local $sCmd = 'powershell -NoProfile -ExecutionPolicy Bypass -Command ' & $powerShell Else Local $sCmd = 'powershell -NoProfile -ExecutionPolicy Bypass -NoExit -Command ' & $powerShell EndIf RunWait($sCmd, "", @SW_SHOW) RegWrite("HKLM\Software\WebLinks","Class", "REG_SZ", $class) EndIf Else WriteLog("No Powershell command found") EndIf ;If we are running, delete any existing links first FileDelete(@DesktopDir & "\\*.url") If Not $silent Then ProgressSet(30,"Looking for Group URLs") Sleep(2000) WriteLog("Looking for group URLs - processing now...") ;Check if Group 1 link defined. If not no groups to progess If $oCustom.Exists("group 1 Link") Then ;We have group links $link=1 Do $gUrl=Json_ObjGet($oCustom, "group " & $link & " Link") If $gUrl="" Or @error Then WriteLog("No more group links found, exiting creation loop") ExitLoop EndIf If Not $silent Then ProgressSet(40,"Creating Group URLs") CreateInternetShortcut("GROUP " & $link, $gUrl, @ScriptDir & "\Group.ico") WriteLog("Created group " & $link & " link to " & $gUrl) $link += 1 Sleep(500) Until $link>20 If FileExists(@ScriptDir & "\ReIcon_x64.exe") And $link>1 Then If Not $silent Then ProgressSet(50,"Repositioning group icons") Sleep(2000) Run(@ScriptDir & "\ReIcon_x64.exe /R /ID rob",@ScriptDir & "\") WriteLog("Group icon positions have been restored") EndIf Else ProgressSet(40,"No Group links discovered") WriteLog("No Group links discovered") EndIf ProgressSet(50,"Looking for custom URLs") Sleep(2000) WriteLog("Looking for custom URLs - processing now...") ;Check if URL 1 link defined. If not no custom groups to process If $oCustom.Exists("url 1 Link") Then $link=1 Do $gUrl=Json_ObjGet($oCustom, "url " & $link & " Link") If ($gUrl=Null Or $gUrl="") And $Instructor Then ;No Custom url in metadata, check in Real API for instructor only $gUrl=GetCSParam($userdata,"URL " & $link & " Link:") EndIf If $gUrl="" Or @error Then WriteLog("No more custom links found, exiting creation loop") ExitLoop EndIf ;MsgBox(0,"URL Link " & $link,$gUrl) ConsoleWrite("Found custom URL: "&$gUrl & @CRLF) $name="Custom " & $link $newname=Json_ObjGet($oCustom, "url " & $link & " Name") If ($newname=Null Or $newname="") And $Instructor Then ;No Custom name in metadata, check in Real API for instructor only $newname=GetCSParam($userdata,"URL " & $link & " Name:") EndIf If Not @error And StringLen($newname)>0 Then $name=$newname EndIf ProgressSet(85,"Creating the custom URLs") CreateInternetShortcut($name, $gUrl, @ScriptDir & "\Custom.ico") WriteLog("Created "& $name & " link to " & $gUrl) Sleep(2000) $link += 1 Sleep(500) Until $link>3 Else ProgressSet(60,"No custom URLs discovered") WriteLog("No custom URLs discovered") EndIf ProgressSet(70,"Looking for individual URLs") Sleep(2000) WriteLog("Looking for individual URLs - processing now...") ;Check if Individual 1 link defined. If not no custom groups to process If $oCustom.Exists("individual 1 Link") Then ;Different approach here - which machine are we and grab correct link $link=Json_ObjGet($oCustom, "studNum") $gUrl="" ;0/null is instructor, 1 upwards is student If $link=0 And $Instructor Then ;metadata instructor info so use it $gUrl=Json_ObjGet($oCustom, "instructor Link") If $gUrl=Null Or $gUrl="" Then ;No Individual url in metadata, not available for students in real API WriteLog("No Instrucror link found in metadata") Else ConsoleWrite("Found Instructor URL: " & $gUrl & @CRLF) $name="Instructor URL" $newname=Json_ObjGet($oCustom, "individual URL Name") If Not @error And StringLen($newname)>0 Then $name=$newname EndIf EndIf Else ;assuming a student link and only in metadata $gUrl=Json_ObjGet($oCustom, "individual " & $link & " Link") If $gUrl=Null Or $gUrl="" Then ;No Individual url in metadata, not available for students in real API WriteLog("No individual link found for VM " & $link) Else ;MsgBox(0,"URL Link " & $link,$gUrl) ConsoleWrite("Found individual URL: " & $gUrl & @CRLF) $name="Individual " & $link $newname=Json_ObjGet($oCustom, "individual URL Name") If Not @error And StringLen($newname)>0 Then $name=$newname EndIf EndIf EndIf If StringLen($gUrl)>5 Then ProgressSet(75,"Creating the Individual URL") CreateInternetShortcut($name, $gUrl, @ScriptDir & "\Individual.ico") WriteLog("Created "& $name & " link to " & $gUrl) Sleep(2000) $link += 1 Sleep(500) Else WriteLog("Something went wrong or non existant individual URL") EndIf Else ProgressSet(80,"No individual URLs discovered") WriteLog("No individual URLs discovered") EndIf ;Drop userdata MD5 into registry to prevent rerun ;RegWrite("HKLM\Software\WebLinks","MD5","REG_SZ",$hash) RegWrite("HKLM\Software\WebLinks","ClassID", "REG_SZ", $class) If Not $silent Then ProgressSet(100,"Web Links deployment completed. Exiting...") Sleep(10000) ProgressOff() EndIf WriteLog("Web Links deployment completed. Exiting...") Exit ; ====================================================================================== ; FUNCTIONS USED ; ====================================================================================== Func GetCSParam($sData,$sKey) If StringInStr($sData,$sKey) Then ; Trim leading/trailing whitespace around the whole string $sData = StringStripWS($sData, 3) ; Regex tolerant of spaces around colon/value/comma Local $sPattern = $sKey & "\s*([^,]+)" ; Run regex search Local $aMatch = StringRegExp($sData, $sPattern, 1) ; If found, return the captured value, else return empty If @error Or UBound($aMatch) = 0 Then Return SetError(1,0,"") ; nothing found Else ;MsgBox(0,"Initial Result for " & $sKey,'"'&$aMatch[0]&'"') $data=StringStripWS($aMatch[0],1) ;If Not @Compiled Then ProgressOff() ;MsgBox(0,"Initial Result for " & $param,'"'&$data&'"') If StringInStr($data,',') Then $data=StringLeft($data,StringInStr($data,',')-1) ;MsgBox(0,"Cut Result for " & $param,$data) If StringInStr($data,@LF) Then ;trim to right from space ;MsgBox(0,"",StringInStr($data,@LF)) $data=StringLeft($data,StringInStr($data,@LF)-1) EndIf ;MsgBox(0,"Final Result for " & $param,$data) Return($data) EndIf Else Return SetError(1,0,"") EndIf EndFunc ;==>GetCSParam ;Create a URL shortcut - optional icon file called group.ico Func _URLCreateShortcut($sUrl, $sName, $icon="", $sFldr = @DesktopDir) ;ConsoleWrite("Created " & $sName & " link to " & $sUrl & @CR) $sFldr = StringRegExpReplace($sFldr, "\\+\z", "") & "\" If NOT FileExists($sFldr) Then Return SetError(1) $sName = StringReplace($sName, ".url", "") & ".url" Local $Ini = $sFldr & $sName, $iSec = "InternetShortcut" $sUrl = StringRegExpReplace($sUrl, "^.*?:/+", "") IniWrite($Ini, $iSec, "URL", $sURL) IniWrite($Ini, $iSec, "IDList", "") If $icon <> "" And FileExists($icon) Then IniWrite($Ini, $iSec, "IconFile", $icon) Else IniWrite($Ini, $iSec, "IconFile", "") EndIf IniWrite($Ini, $iSec, "IconIndex", "1") EndFunc ;==>_URLCreateShortcut ;alternative way of doing the above Func CreateInternetShortcut($sName, $sUrl, $icon = "") ;ConsoleWrite("Created " & $sName & " link to " & $sUrl & @CR) Local $shortcutPath = @DesktopDir & "\" & $sName & ".url" Local $content = "[InternetShortcut]" & @CRLF & _ "URL=" & $sUrl & @CRLF ; Optional icon If $icon <> "" And FileExists($icon) Then ;ConsoleWrite($icon & @CR) $content &= "IconFile=" & $icon & @CRLF & "IconIndex=0" & @CRLF EndIf ; Write the file FileDelete($shortcutPath) ; Overwrite if exists FileWrite($shortcutPath, $content) EndFunc