' // ***************************************************************************
' // 
' // Copyright (c) Microsoft Corporation.  All rights reserved.
' // 
' // Microsoft Deployment Toolkit Solution Accelerator
' //
' // File:      WizUtility.vbs
' // 
' // Version:   6.3.8456.1000
' // 
' // Purpose:   Common Utility functions used by wizard UI Scripts
' // 
' // ***************************************************************************


Function BrowseForFolder(sDefaultValue)
	Dim iRetVal


	iRetVal = Success

	' Workaround for BrowseForFolder problem when called from an HTA:  sometimes it doesn't show up.

	oEnvironment.Item("DefaultFolderPath") = sDefaultValue
	iRetVal = oShell.Run("wscript.exe """ & oUtility.ScriptDir & "\LTIGetFolder.wsf""", 1, true)
	If iRetVal = 0 then
		BrowseForFolder = oEnvironment.Item("FolderPath")
	Else
		BrowseForFolder = sDefaultValue
	End if


End Function



'
' Call a VBScript and handle the errors
'

Function DisplayErrorIfAny ( sStatement )
	Dim sError

	If Err.Number = 0 then
		Exit function
	End if

	'sError = sError & "Use Ctrl-C to copy the text of this message to the clipboard!" &  vbNewLine
	sError = sError & "A VBScript Runtime Error has occurred: " & vbNewLine & vbNewLine
	sError = sError & "Error: " & Err.Number & " = " & Err.Description & vbNewLine & vbNewLine
	sError = sError & "VBScript Code:" & vbNewLine & "-------------------" &  vbNewLine
	sError = sError & left(sStatement,800)

	oLogging.CreateEntry  sError ,LogTypeError

	If oLogging.Debug then
		sError = sError & vbNewLine & "-------------------" &  vbNewLine
		sError = sError & "Do you wish to attempt debugging on this script?"
		DisplayErrorIfAny = MsgBox ( sError, vbYesNo , "VBScript Runtime Error" ) = vbYes
	Else
		Alert sError
		DisplayErrorIfAny = FALSE
	End if

End function


Function ExecuteWithErrorHandling ( statements )
	Dim RunAgain

	RunAgain = FALSE

	On error resume Next
	Err.Clear
	ExecuteGlobal statements
	ExecuteWithErrorHandling = err.number = 0

	RunAgain = DisplayErrorIfAny (statements)
	On error goto 0

	If RunAgain then
		ExecuteGlobal statements
	End if

End function


Function EvalWithErrorHandling ( fn )

	RunAgain = FALSE

	On error resume Next
	Err.Clear
	EvalWithErrorHandling = eval(fn)
	EvalWIthErrorHandling = EvalWIthErrorHandling and (err.number = 0)

	RunAgain = DisplayErrorIfAny (fn)
	On error goto 0

	If RunAgain then
		EvalWithErrorHandling = eval(fn)
	End if

End function


'
' Create an XML Document Node with assoticated Elements and Attributes
'
'  Parameters:
'      oXMLDoc - XML DOM object ( Created from MSXML2.DOMDocument )
'  oTargetNode - XML Document Element where the element is created
'    sNodeName - Name of the Element Created
'  oAttributes - VBScript Dictionary object containing a list of XML Attributes to add
'    oElements - VBScript Dictionary object containing a list of XML Elements to add
'
'  Example Usage:
'   dim     xmlDoc, oAttributes, oElements
'   set  oAttributes = CreateObject("Scripting.Dictionary")
'   set    oElements = CreateObject("Scripting.Dictionary")
'   Set       xmlDoc = CreateXMLDOMObject
'
'   xmlDoc.Load "c:\SomeFile.xml"     or   xmlDoc.appendChild xmlDoc.createElement("MyRootElement")
'
'   oAttributes.Add "guid", left( CreateObject("Scriptlet.TypeLib").GUID, 38 ) ' Strip trailing NULL's
'     oElements.Add "Name","BuildName.value"
'     oElements.Add "Version","1.0"
'
'   CreateXMLNode xmlDoc, xmlDoc.documentElement, "MyElement", oAttributes, oElements
'
'   xmlDoc.Save "c:\SomeFile.xml"
'

Function CreateXMLNode ( xmlDoc, oTargetNode, sNodeName, oAttributes , oElements)

	Dim Key, oElement, oNodeRoot

	Set oNodeRoot = xmlDoc.createElement(sNodeName)

	For each key in oAttributes
		If IsEmpty(key) then
			Exit For
		End if
		oNodeRoot.setAttribute key, oAttributes.Item(key)
	Next

	For each key in oElements.Keys
		If isempty(key) then exit for
		Set oElement = xmlDoc.createElement(key)
		oElement.text = oElements.Item(key)
		oNodeRoot.appendChild oElement
	Next

	oTargetNode.appendChild oNodeRoot

End function


'
' hide/unhide an DHTML element
'
Sub HideUnhide ( oHTMLElement, isVisible )

	If isVisible then
		oHTMLElement.style.display = "inline"
	Else
		oHTMLElement.style.display = "none"
	End if

End sub


'
' Copy Files to a folder
'
'  More information:
'      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/objects/folder/copyhere.asp
'
' WARNING: There is no way to tell if the user hits the Cancel Button during copy.
'
' Parameters:
'       Source - Item or items to copy. Can be a file name string, a FolderItem object, or a FolderItems object.
'     sDestDir - Target Destination Directory. If missing then this procedure will create the directory.
'        Flags - Copy Flags for CopyHere (Typical settings: 2048 or 16 ===> 2064 ):
'   CleanFirst - Clean the directory first.
'
Sub CopyFileWithProgressEx ( Source , sDestDir, Flags, CleanFirst )

	Dim objFolder, hr

	If CleanFirst and oFso.FolderExists( sDestDir ) then
		oFSO.DeleteFolder sDestDir
	End if

	' Open the Destination Directory (Create if missing)
	oUtility.VerifyPathExists sDestDir
	Set objFolder = CreateObject("Shell.Application").NameSpace(sDestDir)

	If objFolder is nothing then
		Err.Raise 507,,"Destination Folder not set: " & sDestDir
		Exit sub
	End if

	objFolder.CopyHere Source, Flags
	Set objFolder = nothing

End sub


Sub CopyFileWithProgress ( Source , sDestDir )
	' Common Settings
	'    16 Respond with "Yes to All" for any dialog box that is displayed.
	'  2048 Version 4.71. Do not copy the security attributes of the file.
	CopyFileWithProgressEx Source , sDestDir, 2064, TRUE
End sub

'
' Tests a filename for invalid characters.
'
Function IsValidFileName (FileName)

	Dim regEx

	Set regEx = New RegExp
	regEx.Pattern = "[\x00-\x1F\<\>\:\""\/\\\|\%\*\?\']"   'Strict Subset
	IsValidFileName = (not regEx.Test ( FileName )) and (trim(FileName) <> "") and len(trim(FileName)) <= 253

	Select Case UCase(Trim(FileName))
	Case "CON", "AUX", "COM1", "COM2", "COM3", "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL"
		IsValidFileName = FALSE
	End select

End function


Function IsValidPath (FilePath)

	Dim regEx

	Set regEx = New RegExp
	regEx.Pattern = "[\x00-\x1F\<\>\""\%\*\?\']"   'Strict Subset
	IsValidPath = (not regEx.Test ( FilePath )) and (trim(FilePath) <> "") and len(trim(FilePath)) <= 253

End function


Function IsValidComputerName ( OSDComputerName )

	Dim regEx
	Set regEx = New RegExp
	regEx.Pattern = "[^a-zA-Z0-9\-\_]"   'Strict Subset
	' regEx.Pattern = "[^a-zA-Z0-9\!\@\#\$\%\^\&\'\)\(\.\-\_\{\}\~ ]" ' SetComputerName compliant
	IsValidComputerName 	= not regEx.Test ( OSDComputerName ) and len(OSDComputerName) <= 15

End function



Sub AddToList(byref List, Item, Delimiter)
	oStrings.AddToList List,Item,Delimiter
End sub

Function ForceAsString ( InputVar )
	ForceAsString = oStrings.ForceAsString ( InputVar )
End function

Function IsWhiteSpace (MyChar)
	IsWhiteSpace = oStrings.IsWhiteSpace(MyChar)
End function


Function TrimAllWS( MyString )
	TrimAllWS = oStrings.TrimAllWS( MyString )
End function


'
' Validate Credentials against either a Domain or a UNC Disk Resource
'   Function will return TRUE if successfull, otherwise it will return an ERROR string!
'
Function CheckCredentials(sValidateUNC, sUserName, sDomain, sPassword)

	Dim sFullUserName
	Dim sJustUserName
	Dim sUserObjectPath
	Dim oWinNT
	Dim oDomain ' As IADsDomain
	Dim sDriveLetter


	sFullUserName = sUserName
	If sDomain <> "" then
		sFullUserName = sDomain & "\" & sFullUserName
	End if


	If sValidateUNC = "" then

		If oEnvironment.Item("ValidateAgainstUNCPath") <> "" then
			sValidateUNC = oEnvironment.Item("ValidateAgainstUNCPath")
		Else

			sValidateUNC = oEnvironment.Item("DeployRoot")
			For Each oDomain in objWMI.InstancesOf("Win32_ComputerSystem")
				if oDomain.DomainRole = 1 or oDomain.DomainRole = 3 or oDomain.DomainRole = 4 or oDomain.DomainRole = 5 then
					oLogging.CreateEntry "Computer is part of a domain, valiadate against domain.", LogTypeInfo
					sValidateUNC = "" 
				End if
			next

		End if

	End if


	If sValidateUNC <> "" then

		'
		' Validate the credentials against an actual UNC disk resource
		'

		sDriveLetter = oUtility.MapNetworkDriveEx (sValidateUNC, sFullUserName, sPassword, LogTypeInfo )

		If len(sDriveLetter) = 2 then
			If bLeaveShareOpen <> TRUE and ucase(sValidateUNC) <> ucase(oEnvironment.Item("DeployRoot")) then
				oNetwork.RemoveNetworkDrive sDriveLetter
			End if
			CheckCredentials = TRUE
		Else
			CheckCredentials = sDriveLetter ' If not a drive letter, then this is an error string
		End if

	Else
	
		CheckCredentials = CheckCredentialsAD( "", sUserName, sDomain, sPassword)

	End if

	If CheckCredentials <> TRUE then
		oLogging.CreateEntry "Credentials Script: " & CheckCredentials, LogTypeInfo
		Exit function
	End if

	oEnvironment.Item("UserCredentials") = Cstr(TRUE)

	If bDoNotSaveParameters = TRUE then
		window.close
		CheckCredentials = FALSE
	End if

End Function

'
' Validate Credentials against a Domain
'   Function will return TRUE if successfull, otherwise it will return an ERROR string!
'
Function CheckCredentialsAD(sJoinDomain, sUserName, sDomain, sPassword)

	Dim sFullUserName
	Dim sJustUserName
	Dim sUserObjectPath
	Dim oWinNT
	Dim oDomain ' As IADsDomain
	Dim sDriveLetter
	Dim sJoinDomainNew
	
	sJoinDomainNew = sJoinDomain


	sFullUserName = sUserName
	If sDomain <> "" then
		sFullUserName = sDomain & "\" & sFullUserName
	End if


	'
	' Validate the credentials against a domain or computer server using Active Directory authentication.
	'

	' The credentials can be in the form "BillG", "redmond\BillG", or "BillG@Microsoft.Com". Cleanup for use.

	sJustUserName = sUserName
	If Instr(1,sJustUserName,"@") <> 0 then
		' Username is in form: BillG@redmond.corp.Microsoft.com, remove domain.
		sJustUserName = left(sJustUserName, instr(1,sJustUserName,"@") - 1)
	ElseIf instr(1,sJustUserName,"\") <> 0 then
		' Username is in form: redmond\BillG, remove domain.
		sJustUserName = mid(sJustUserName, instr(1,sJustUserName,"\") + 1 )
	End if

	On Error Resume Next
	Set oWinNT = GetObject("WinNT:")
	If Err then
		If oEnvironment.Item("OSVersion") = "WinPE" then
			oLogging.CreateEntry "Unable to verify domain credentials in Windows PE since ADSI is not available", LogTypeInfo
			CheckCredentialsAD = TRUE
			Err.Clear
			Exit Function
		Else
			CheckCredentialsAD = Err.Description & " (" & Hex(Err.Number) & ")"
			Exit Function
		End if
	End if



	If sJoinDomainNew = "" then
		sJoinDomainNew = sDomain
	ElseIf Instr(1, sUserName, "\") <> 0 then
		sJoinDomainNew = left(sUserName, instr(1, sUserName, "\") - 1)
	ElseIf Instr(1, sUserName, "@") <> 0 then
		sJoinDomainNew = mid(sJustUserName, instr(1,sJustUserName,"@") + 1 )
	End if


	' 1 = ADS_SECURE_AUTHENTICATION
	Set oDomain = oWinNT.OpenDSObject("WinNT://" & sJoinDomainNew & "/" & sJustUserName &  ",user" ,  sFullUserName, sPassword, 1 )
		


	If Err.Number = &h80070035 then
		CheckCredentialsAD = "Network path not found (80070035)"
	ElseIf Err.Number = &H8007054B then
		CheckCredentialsAD = "Domain could not be contacted (8007054B)"
	ElseIf Err.Number = &h8007052E then
		CheckCredentialsAD = "User ID or password is invalid (8007052E)"
	ElseIf Err.Number = &h800708AD then
		CheckCredentialsAD = "User ID is not valid (800708AD)"
	ElseIf Err then
		CheckCredentialsAD = Err.Description & " (" & Hex(Err.Number) & ")"
	ElseIf oDomain is nothing then
		CheckCredentialsAD = "Domain validation failed - " & Err.Description & " (" & Err.Number & ")"
	Else
		Err.Clear
		CheckCredentialsAD = TRUE
	End if
	On error goto 0

	If CheckCredentialsAD <> TRUE then
		oLogging.CreateEntry "Credentials Script: " & CheckCredentialsAD, LogTypeInfo
		Exit function
	End if

	oEnvironment.Item("UserCredentials") = Cstr(TRUE)

	If bDoNotSaveParameters = TRUE then
		window.close
		CheckCredentialsAD = FALSE
	End if

End Function

Function GetDomainDefault
	Dim oComputer


	' Get Local Domain

	GetDomainDefault = ""
	if oEnvironment.Item("UserDomain") <> "" then
		GetDomainDefault = oEnvironment.Item("USERDOMAIN")
	else
		On Error Resume Next
		For each oComputer in objWMI.InstancesOf("Win32_ComputerSystem")
			If oComputer.DomainRole <> 0 then
				GetDomainDefault = oComputer.Domain
			End if
		Next
		On Error Goto 0
	end if

End Function


Function GetDestDisk

	' Preference search order is: oProperties, oEnvironment/CS.INI , and TS.XML ( DefaultDestinationXxx )
	GetDestDisk = Property("DestinationDisk")
	If GetDestDisk = "" then
		GetDestDisk = Property("DefaultDestinationDisk")
	End if
	If GetDestDisk = "" then
		GetDestDisk = "0"
	End if

End function

Function GetDestPart

	' Preference search order is: oProperties, oEnvironment/CS.INI , and TS.XML ( DefaultDestinationXxx )
	GetDestPart = Property("DestinationPartition")
	If GetDestPart = "" then
		GetDestPart = Property("DefaultDestinationPartition")
	End if
	If GetDestPart = "" then
		GetDestPart = "1"
	End if

End function

Function HasGoodDestDisk( sDestDisk )

	Dim oDisk, oDisks

	Set oDisks = objWMI.ExecQuery("Select index from Win32_DiskDrive where MediaType like 'Fixed%hard disk%'")

	HasGoodDestDisk = False
	For Each oDisk in oDisks
		If Cstr(oDisk.Index) = sDestDisk Then
			oLogging.CreateEntry "Validated Disk exists", LogTypeInfo
			HasGoodDestDisk = True
			exit for
		End If
	Next

End function

Function HasGoodDestPart ( sDestDisk, sDestPart )

	Dim sDestDrive
	
	HasGoodDestPart = true
	If Property("DeploymentType") = "REFRESH" Then

		sDestDrive = oUtility.DetermineDriveFromDiskPart( sDestDisk, sDestPart )
		HasGoodDestPart = sDestDrive = oEnv("SystemDrive")
		oLogging.CreateEntry "oUtility.DetermineDriveFromDiskPart( " & sDestDisk& ", " & sDestPart& " )  = " & sDestDrive, LogTypeInfo

	End if

End function


Function RmPropIfFound( Prop )

	If oProperties.Exists(Prop) then
		oProperties.Remove(Prop)
	End if

End function


