 
' // ***************************************************************************
' // 
' // Copyright (c) Microsoft Corporation.  All rights reserved.
' // 
' // Microsoft Deployment Toolkit Solution Accelerator
' //
' // File:      ZTIUtility.vbs
' // 
' // Version:   6.3.8456.1000
' // 
' // Purpose:   Common Libraries for Microsoft Deployment Toolkit
' // 
' // ***************************************************************************


Option Explicit

' Public constants

Public Const ForReading = 1
Public Const ForWriting = 2
Public Const ForAppending = 8

Public Const Success = 0
Public Const Failure = 1

Public Const LogTypeInfo = 1
Public Const LogTypeWarning = 2
Public Const LogTypeError = 3
Public Const LogTypeVerbose = 4
Public Const LogTypeDeprecated = 5

Public Const TextCompare = 1

Public Const adOpenStatic = 3
Public Const adLockReadOnly = 1
Public Const adLockOptimistic = 3

Public Const Version = "6.3.8456.1000"


' Global variables

Dim oShell, oEnv, oNetwork, oFSO, objWMI, oDrive
Dim oUtility, oLogging, oEnvironment
Dim oStrings
Dim oFileHandling

' Initialization code

On Error Resume Next
Set oUtility = New Utility
oUtility.PrepareEnvironment


function PrnErrValue ( iError )
	PrnErrValue = oLogging.FormatError ( iError )
end function


Sub ProcessResults(iRc)
	ProcessResults = oLogging.ProcessResults ( iRc )
End Sub

function RunNewInstance 
	Dim oScriptClass
	RunNewInstance = oUtility.RunNewInstanceEx ( oUtility.ScriptName, "oScriptClass", "oScriptClass.Main" )
end function 


Function TestAndLog ( iRc , sMessage)

	TestAndLog = oLogging.TRACEEX(iRc, "", sMessage, FALSE)

End function

Function TestAndFail ( iRc, iError, sMessage)

	TestAndFail = oLogging.TRACEEX(iRc, iError, sMessage, TRUE)

End function

'//---------------------------------------------------------------------------
'//  Function:	ConvertBooleanToString()
'//  Purpose:	Perform a Cstr operation manually to prevent localization 
'//             from converting True/False to non-english values.
'//---------------------------------------------------------------------------
Function ConvertBooleanToString(bValue)

	ConvertBooleanToString = oUtility.ConvertBooleanToString(bValue)

End Function



' Public classes

' //////////////////////////////////////////////////////

class Logging

	' //
	' // Logging is a public class used for trace logging within the BDD/MDT scripting Environment
	' //
	' // Assumes: oUtility, oEnvironment, oFSO - Must be initialized and active.
	' //


	' ***  Public variables ***

	Public LogFile
	Public MasterLogFile
	Public Component
	Public Debug
	Public NetworkLogging


	' ***  Private variables ***


	' ***  Constructor and destructor ***

	Private Sub Class_Initialize


		' Set file names and paths

		Component = oUtility.ScriptName
		LogFile = Component & ".log"
		MasterLogFile = "BDD.log"
		NetworkLogging = ""


		' Set debug to false, allow PrepareEnvironment to override

		Debug = False


	End Sub


	' ***  Public methods ***

	' //
	' // Standard MDT logging routine, used for most logging operations
	' //
	' //  Standard Error Types
	' //     Public Const LogTypeInfo        = 1   ' Informational Message
	' //     Public Const LogTypeWarning     = 2   ' Warning Message
	' //     Public Const LogTypeError       = 3   ' Error Message
	' //     Public Const LogTypeVerbose     = 4   ' Verbose Messages only logged when Debug has been set.
	' //     Public Const LogTypeDeprecated  = 5   ' Informational Message that is elevated to Error when Debug is set.
	' //

	Public Function CreateEntry(sLogMsg, iType)
		Dim sTime, sDate, sTempMsg, oLog, bConsole


		' Each of the operations below has the potential to cause a runtime error.
		' However, we must not stop operation if there is a failure, so allways continue.

		On Error Resume Next


		' Special Handling for Debug vs. Non-Debug messages

		If not Debug then

			If iType = LogTypeVerbose Then
				Exit Function  ' Verbose Messages are only displayed when Debug = True
			Elseif iType = LogTypeDeprecated Then
				iType = LogTypeInfo ' Deprecated messages are normally Info messages
			End if			

		Else  ' Debug = True

			If iType = LogTypeVerbose then
				iType = LogTypeInfo
			Elseif iType = LogTypeDeprecated Then
				iType = LogTypeError
			End if

		End if

		' Suppress messages containing password

			If Instr(1, sLogMsg, "password", 1) > 0 then
				sLogMsg = "<Message containing password has been suppressed>"
			End if

		' Populate the variables to log

		sTime = Right("0" & Hour(Now), 2) & ":" & Right("0" & Minute(Now), 2) & ":" & Right("0" & Second(Now), 2) & ".000+000"
		sDate = Right("0"& Month(Now), 2) & "-" & Right("0" & Day(Now), 2) & "-" & Year(Now)
		sTempMsg = "<![LOG[" & sLogMsg & "]LOG]!><time=""" & sTime & """ date=""" & sDate & """ component=""" & Component & """ context="""" type=""" & iType & """ thread="""" file=""" & oUtility.ScriptName & """>"


		' Make sure the LogPath directory exists

		oUtility.VerifyPathExistsEx LogPath, False


		' If debug, echo the message

		bConsole = InStr(1,Wscript.FullName,"CSCRIPT.EXE", vbTextCompare ) <> 0
		If bConsole = True then
			Wscript.echo sLogMsg
		End if


		' Create the log entry

		Set oLog = oFSO.OpenTextFile(LogPath & "\" & LogFile, ForAppending, True)
		oLog.WriteLine sTempMsg
		oLog.Close


		' Create the master log entry

		Set oLog = oFSO.OpenTextFile(LogPath & "\" & MasterLogFile, ForAppending, True)
		oLog.WriteLine sTempMsg
		oLog.Close


		' Write to a network Share Entry

		If NetworkLogging <> "" then
			Set oLog = oFSO.OpenTextFile(NetworkLogging & "\" & MasterLogFile, ForAppending, True)
			oLog.WriteLine sTempMsg
			oLog.Close
		End if

		On error goto 0

	End Function

	' //
	' // Standard MDT Event Messaging routine, used To send event status messages to a networked log file.
	' //
	' //  Standard Error Types
	' //     Public Const LogTypeInfo     = 1   ' Informational Message
	' //     Public Const LogTypeWarning  = 2   ' Warning Message
	' //     Public Const LogTypeError    = 3   ' Error Message
	' //     Public Const LogTypeVerbose  = 4   ' Verbose Messages only logged when Debug has been set.
	' //

	Function CreateEvent(iEventID, iType, sMessage, arrParms)

		Dim sEventFile
		Dim fptr
		Dim sLine
		Dim iMOMType
		Dim sDomain
		Dim sComputer
		Dim sPackage
		Dim sAdvert
		Dim i
		Dim sCurrentStep
		Dim sTotalSteps

		Dim sScript
		Dim oService
		Dim sID
		Dim oResult


		On Error Resume Next


		' Log the message specified

		CreateEntry sMessage, iType


		' Get some information from the task sequence environment for use below (if needed)

		sCurrentStep = oEnvironment.Item("_SMSTSNextInstructionPointer")
		If sCurrentStep = "" then
			sCurrentStep = "0"
		End if
		sTotalSteps = oEnvironment.Item("_SMSTSInstructionTableSize")
		If sTotalSteps = "" then
			sTotalSteps = "0"
		End if


		' Send events to the event share if specified

		If oEnvironment.Item("EventShare") <> "" then

			' Make sure the path is accessible

			oUtility.ValidateConnection oEnvironment.Item("EventShare")


			' Create a unique file name on the share.

			sEventFile = oEnvironment.Item("EventShare") & "\" & oUtility.ComputerName & "_" & oFSO.GetTempName
			Set fptr = oFSO.CreateTextFile(sEventFile, True)
			If Err then
				CreateEntry "Unable to create event file " & sEventFile & ": " & Err.Description & " (" & Err.Number & ")", LogTypeError
				Exit function
			End if


			' Build the first line to write

			Select Case iType
			Case LogTypeInfo
				iMOMType = 4
			Case LogTypeWarning
				iMOMType = 2
			Case LogTypeError
				iMOMType = 1
			End Select

			sDomain = oEnvironment.Item("JoinDomain")
			sComputer = oUtility.ComputerName

			If oEnvironment.Item("_SMSTSAdvertID") <> "" then
				sAdvert = oEnvironment.Item("_SMSTSAdvertId")
				sPackage = oEnvironment.Item("_SMSTSPackageID")
			ElseIf oEnvironment.Item("OSDADVERTID") = "" then
				sAdvert = "OSD00000"
				sPackage = "OSD00000"
			Else
				sAdvert = oEnvironment.Item("OSDADVERTID")
				sPackage = oEnvironment.Item("OSDPACKAGEID")
			End if


			' Write out the first line (common content)

			sLine = CStr(iEventID) & "," & CStr(iMOMType) & "," & sDomain & "," & sComputer & "," & sPackage & "," & sAdvert & "," & sCurrentStep & "," & sTotalSteps & "," & oEnvironment.Item("DeploymentMethod")
			fptr.WriteLine sLine
			If Err then
				CreateEntry "Unable to write event file " & sEventFile & ": " & Err.Description & " (" & Err.Number & ")", LogTypeError
				Exit function
			End if


			' Write out the second line (insertion strings)

			fptr.WriteLine Join(arrParms, ",")
			If Err then
				CreateEntry "Unable to write event file " & sEventFile & ": " & Err.Description & " (" & Err.Number & ")", LogTypeError
				Exit function
			End if


			' Write out the third line (message)

			fptr.WriteLine sMessage
			If Err then
				CreateEntry "Unable to write event file " & sEventFile & ": " & Err.Description & " (" & Err.Number & ")", LogTypeError
				Exit function
			End if


			' Close the file

			fptr.Close
			Set fptr = Nothing

			CreateEntry "Event " & CStr(iEventID) & " sent: " & sMessage, LogTypeInfo

		End if


		' Send events to the event service if specified

		If oEnvironment.Item("EventService") <> "" then

			' Create a web service object

			Set oService = Nothing
			Err.Clear
			Set oService = new WebService
			If Err then

				' Load ZTIDataAccess.vbs and try again
				sScript = oFSO.OpenTextFile(oEnv("ScriptRoot") & "\ZTIDataAccess.vbs", 1, false).ReadAll
				ExecuteGlobal sScript

				Set oService = new WebService

			End if

			If oService is Nothing then
				oLogging.CreateEntry "Unable to create WebService class", LogTypeWarning
				Exit Function
			End if


			' Build the parameters to pass to the web service

			sService = "PostEvent"
			sID = oEnvironment.Item("UUID") & "," & Join(oEnvironment.ListItem("MacAddress").Keys, ",")
			sLine = "uniqueID=" & oEnvironment.Item("LTIGUID") & "&computerName=" & oUtility.ComputerName & "&messageID=" & CStr(iEventID) & "&severity=" & CStr(iType) & "&stepName=" & oEnvironment.Item("_SMSTSCurrentActionName") & "&currentStep=" & sCurrentStep & "&totalSteps=" & sTotalSteps & "&id=" & sID & "&message=" & sMessage
			sLine = sLine & "&dartIP=" & oEnvironment.Item("DartIP001") & "&dartPort=" & oEnvironment.Item("DartPort001") & "&dartTicket=" & oEnvironment.Item("DartTicket") & "&vmHost=" & oEnvironment.Item("VMHost") & "&vmName=" & oEnvironment.Item("VMName")


			' Call the web service

			oService.WebService = oEnvironment.Item("EventService") & "/MDTMonitorEvent/PostEvent?" & sLine
			oService.Method = "GET"
			oService.Quiet = true
			Set oResult = oService.Query


			' Remember the returned GUID value

			If oEnvironment.Item("LTIGUID") <> oResult.DocumentElement.Text then
				oEnvironment.Item("LTIGUID") = oResult.DocumentElement.Text
			End if


			CreateEntry "Event " & CStr(iEventID) & " sent: " & sMessage, LogTypeInfo

		End if


	End Function



	Function GetDiscoveryArray

		Dim sLine
		Dim dicMac
		Dim arrMac
		Dim i


		' Add the local entries

		sLine = oEnvironment.Item("PHASE") & "," & oEnvironment.Item("AssetTag") & "," & oEnvironment.Item("UUID")
		Set dicMac = oEnvironment.ListItem("MacAddress")
		arrMac = dicMac.Keys

		For i = 0 to 4
			If i > UBound(arrMac) then
				sLine = sLine & ","
			Else
				sLine = sLine & "," & arrMac(i)
			End if
		Next


		' Add the user data entries (which might not be available yet)

		sLine = oEnvironment.Substitute(sLine & "," & oEnvironment.Item("SLShare") & "," & oEnvironment.Item("UDShare") & "," & oEnvironment.Item("UDDir"))


		' Split it into an array and return it

		GetDiscoveryArray = Split(sLine,",")

	End Function


	Public Function CopyLog()

		Dim oFile
		Dim iRetVal, fptr1, fptr2, sLine, sNewLogFolderName, sLogFile
		Dim sComputer
		Dim sLog
		Dim sBootDrive

		On Error Resume Next


		' Figure out where to copy the local logfile to

		If oEnvironment.Item("SLShare") = "" then
			oLogging.CreateEntry "Unable to copy log to the network as no SLShare value was specified.", LogTypeInfo
			Exit Function
		End if


		' Make sure the path is accessible
		oUtility.ValidateConnection oEnvironment.Item("SLShare")
		oUtility.VerifyPathExists oEnvironment.Item("SLShare")
		If not oFSO.FolderExists(oEnvironment.Item("SLShare")) then
			oLogging.CreateEntry "An invalid SLShare value of " & oEnvironment.Item("SLShare") & " was specified.", LogTypeWarning
			Exit Function
		End if


		' Figure out the computer name

		sComputer = oUtility.ComputerName

		sBootDrive = oUtility.GetOSTargetDriveLetterEx(false) 
		if sBootDrive = "" then
			oLogging.CreateEntry "Destination Logical Drive was not specificed, defaulting to #:\", LogTypeInfo
			sBootDrive = "#:"  ' Unknown Boot Drive
		End if


		' Construct the new folder name

		sNewLogFolderName = oEnvironment.Item("SLShare") & "\" & sComputer
		oUtility.VerifyPathExists sNewLogFolderName


		' Copy all the main logs (except BDD.LOG, which we'll merge later)

		For each oFile in oFSO.GetFolder(oLogging.LogPath).Files
			If UCase(oFile.Name) <> "BDD.LOG" and (Right(UCase(oFile.Name), 4) = ".LOG" or Right(UCase(oFile.Name), 4) = ".XML") then
				oLogging.CreateEntry "Copying " & oLogging.LogPath & "\" & oFile.Name & " to " & sNewLogFolderName & "\" & oFile.Name, LogTypeInfo
				oFSO.CopyFile oLogging.LogPath & "\" & oFile.Name, sNewLogFolderName & "\", True			
			End if
		Next


		' Copy any exceptions we can find

		For each sLog in Array("SMSTS.LOG", "Debug\Netsetup.log", "wpeinit.log", "Debug\DCPROMO.LOG", "Debug\DCPROMOUI.LOG", "OSDSetupWizard.log")

			If oFSO.FileExists(oEnvironment.Item("SMSTSLogPath_Cache") & "\" & sLog) then
				oLogging.CreateEntry "Copying " & oEnvironment.Item("SMSTSLogPath_Cache") & "\" & sLog & " to " & sNewLogFolderName & "\" & sLog, LogTypeInfo
				oFSO.CopyFile oEnvironment.Item("SMSTSLogPath_Cache") & "\" & sLog, sNewLogFolderName & "\", True
			ElseIf oFSO.FileExists(oEnvironment.Item("_SMSTSLogPath") & "\" & sLog) then
				oLogging.CreateEntry "Copying " & oEnvironment.Item("_SMSTSLogPath") & "\" & sLog & " to " & sNewLogFolderName & "\" & sLog, LogTypeInfo
				oFSO.CopyFile oEnvironment.Item("_SMSTSLogPath") & "\" & sLog, sNewLogFolderName & "\", True
			ElseIf oFSO.FileExists(oEnv("TEMP") & "\" & sLog) then
				oLogging.CreateEntry "Copying " & oEnv("TEMP") & "\" & sLog & " to " & sNewLogFolderName & "\" & sLog, LogTypeInfo
				oFSO.CopyFile oEnv("TEMP") & "\" & sLog, sNewLogFolderName & "\", True
			ElseIf oFSO.FileExists(oEnv("SystemRoot") & "\" & sLog) then
				oLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\" & sLog & " to " & sNewLogFolderName & "\", LogTypeInfo
				oFSO.CopyFile oENV("SystemRoot") & "\" & sLog,  sNewLogFolderName & "\", True
			ElseIf oFSO.FileExists(oEnv("SystemRoot") & "\System32\" & sLog) then
				oLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\System32\" & sLog & " to " & sNewLogFolderName & "\", LogTypeInfo
				oFSO.CopyFile oENV("SystemRoot") & "\System32\" & sLog,  sNewLogFolderName & "\", True
			End if

		Next


		' Copy the Panther Logs

		If oFSO.FileExists(oENV("SystemRoot") & "\Panther\setupact.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\Panther\setupact.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile oENV("SystemRoot") & "\Panther\setupact.log",  sNewLogFolderName & "\Panther\", True
		End If

		If oFSO.FileExists(oENV("SystemRoot") & "\Panther\setuperr.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\Panther\setuperr.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile oENV("SystemRoot") & "\Panther\setuperr.log",  sNewLogFolderName & "\Panther\", True
		End If

		If oFSO.FileExists(oENV("SystemRoot") & "\Panther\cbs_unattend.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\Panther\cbs_unattend.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile oENV("SystemRoot") & "\Panther\cbs_unattend.log",  sNewLogFolderName & "\Panther\", True
		End If

		If oFSO.FileExists(oENV("SystemRoot") & "\Panther\UnattendGC\setupact.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther\UnattendGC"
			OLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\Panther\UnattendGC\setupact.log to " & sNewLogFolderName & "\Panther\UnattendGC", LogTypeInfo
			oFSO.CopyFile oENV("SystemRoot") & "\Panther\UnattendGC\setupact.log",  sNewLogFolderName & "\Panther\UnattendGC\", True
		End If

		If oFSO.FileExists(oENV("SystemRoot") & "\Panther\UnattendGC\setuperr.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther\UnattendGC"
			OLogging.CreateEntry "Copying " & oENV("SystemRoot") & "\Panther\UnattendGC\setuperr.log to " & sNewLogFolderName & "\Panther\UnattendGC", LogTypeInfo
			oFSO.CopyFile oENV("SystemRoot") & "\Panther\UnattendGC\setuperr.log",  sNewLogFolderName & "\Panther\UnattendGC\", True

		End If			

		If oFSO.FileExists(sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setupact.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setupact.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setupact.log",  sNewLogFolderName & "\Panther\", True
		End If

		If oFSO.FileExists(sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setuperr.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setuperr.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile sBootDrive & "\$WINDOWS.~BT\Sources\Panther\setuperr.log",  sNewLogFolderName & "\Panther\", True
		End If
		
		If oFSO.FileExists(sBootDrive & "\$WINDOWS.~BT\Sources\Panther\cbs_unattend.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther"
			OLogging.CreateEntry "Copying " & sBootDrive & "\$WINDOWS.~BT\Sources\Panther\cbs_unattend.log to " & sNewLogFolderName & "\Panther", LogTypeInfo
			oFSO.CopyFile sBootDrive & "\$WINDOWS.~BT\Sources\Panther\cbs_unattend.log",  sNewLogFolderName & "\Panther\", True
		End If
		
		If oFSO.FileExists(sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setupact.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther\UnattendGC"
			OLogging.CreateEntry "Copying " & sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setupact.log to " & sNewLogFolderName & "\Panther\UnattendGC", LogTypeInfo
			oFSO.CopyFile sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setupact.log",  sNewLogFolderName & "\Panther\UnattendGC\", True
		End If

		If oFSO.FileExists(sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setuperr.log") Then

			oUtility.VerifyPathExists sNewLogFolderName & "\Panther\UnattendGC"
			OLogging.CreateEntry "Copying " & sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setuperr.log to " & sNewLogFolderName & "\Panther\UnattendGC", LogTypeInfo
			oFSO.CopyFile sBootDrive & "\$WINDOWS.~BT\Sources\Panther\UnattendGC\setuperr.log",  sNewLogFolderName & "\Panther\UnattendGC\", True

		End If					



		' Make sure we have a local log file; it might not exist if the disk isn't yet writable.

		sLogFile = LogPath & "\" & MasterLogFile
		If not oFSO.FileExists(sLogFile) then
			oLogging.CreateEntry "Master log file " & sLogFile & " was not found, unable to copy to " & sNewLogFolderName & "\BDD.LOG", LogTypeInfo
			Exit Function
		End if


		' Copy the file contents to the end of the network file. (It might already exist from a previous action, so append to it.)

		oLogging.CreateEntry "Copying log " & sLogFile & " contents to " & sNewLogFolderName & "\BDD.LOG", LogTypeInfo

		Set fptr1 = oFSO.OpenTextFile(sLogFile, ForReading, True)
		If Err then
			oLogging.CreateEntry "Unable to open " & sLogFile & " for reading: " & Err.Description & " (" & Err.Number & ")", LogTypeInfo
			Err.Clear
			Exit Function
		End if

		Set fptr2 = oFSO.OpenTextFile(sNewLogFolderName & "\BDD.LOG", ForAppending, True)
		If Err then
			oLogging.CreateEntry "Unable to open " & sNewLogFolderName & "\BDD.LOG for appending: " & Err.Description & " (" & Err.Number & ")", LogTypeInfo
			Err.Clear
			Exit Function
		End if

		Do while Not fptr1.AtEndOfStream
			sLine = fptr1.readline
			fptr2.writeline sLine
		Loop

		fptr1.Close
		fptr2.Close


		Err.Clear
		On Error Goto 0

	End Function


	Public Function ReportProgress(sMsg, iPercent)

		Dim iMaxPercent
		Dim oProgress
		Dim uStep
		Dim uMaxStep

		CreateEntry "Update progress [ " & iPercent & " ] : " & sMsg , LogTypeVerbose


		' Try to create the progress UI object

		On Error Resume Next
		Set oProgress = CreateObject("Microsoft.SMS.TSProgressUI")
		If Err then
			Err.Clear

			' Record the progress in the registry

			oShell.RegWrite "HKLM\Software\Microsoft\Deployment 4\ProgressPercent", iPercent, "REG_DWORD"
			oShell.RegWrite "HKLM\Software\Microsoft\Deployment 4\ProgressText", sMsg, "REG_SZ"

			on error goto 0
			Exit Function
		End if
		On Error Goto 0


		' Update the progress

		On Error Resume Next

		iMaxPercent = 100
		uStep = CLng(oEnvironment.Item("_SMSTSNextInstructionPointer"))
		uMaxStep = CLng(oEnvironment.Item("_SMSTSInstructionTableSize"))
		Call oProgress.ShowActionProgress(oEnvironment.Item("_SMSTSOrgName"), oEnvironment.Item("_SMSTSPackageName"), oEnvironment.Item("_SMSTSCustomProgressDialogMessage"), oEnvironment.Item("_SMSTSCurrentActionName"), (uStep), (uMaxStep), sMsg, (iPercent), (iMaxPercent))
		If Err then
			CreateEntry "Unable to update progress: " & Err.Description & " (" & Err.Number & ")", LogTypeInfo
			ReportProgress = Failure
			Err.Clear
			Exit Function
		End if

		On Error Goto 0


		' Dispose of the object

		Set oProgress = Nothing

	End Function


	Property Get LogPath

		Dim iRetVal
		LogPath = oUtility.LogPath
		'Preserve the existing logpath before creating the new logpath
		
		If Ucase(oUtility.ScriptName) = "ZTIDISKPART" and Left(LogPath,2)<> "X:" Then
			oUtility.VerifyPathExistsEx "X:\MININT\SMSOSD\OSDLOGS", FALSE
			LogPath = "X:\MININT\SMSOSD\OSDLOGS"
		End If
		
		If oEnvironment.Item("LogPath") <> "" AND oEnvironment.Item("LogPath") <> LogPath And Ucase(oUtility.ScriptName) <> "LTICLEANUP" Then

			On Error Resume Next
			iRetVal = oShell.Run("xcopy """ & oEnvironment.Item("LogPath") & """ """ & LogPath & """ /D /s /e /h /y /c",0, true)
			oEnvironment.Item("LogPath") = LogPath
			on Error goto 0

		End If
		If oEnvironment.Item("LogPath") = "" And Ucase(oUtility.ScriptName) <> "LTICLEANUP" Then	

			oEnvironment.Item("LogPath") = LogPath

		End If

	End Property


	Function ReportFailure ( sMessage, iError )

		CreateEvent 41002, LogTypeError, oEnvironment.Substitute( "FAILURE ( " & FormatError(iError) & " ): " & sMessage ), Array(iError)

		' It is possible that we are are not running in the Wscript Host ( HTML Page ).
		on error resume next
			WScript.Quit iError
		on error goto 0

	End function


	' //
	' //  Perform an inline check of the condition and write out the message
	' //    iRC can be either SUCCESS (0), FAILURE(not 0) as defined above.
	' //    or iRC can be Boolean either TRUE or FALSE
	' //  
	' //  Anything other than Success or Failure will log a Warning or Error.
	' //
	Function TRACEEX( iRc, iError, sMessage, bFatal)

		TRACEEX = iRc

		If Err then

			' Error
			TRACEEX = Err.Number
			sMessage = sMessage & " - " & Err.Description
			CreateEntry oEnvironment.Substitute( "FAILURE (Err): " & FormatError(TRACEEX) & ": " & sMessage ), LogTypeWarning
			If bFatal then
				' Fatial Error
				ReportFailure sMessage, iError
			End if

		Elseif (VarType(iRC) = vbInteger) or (VarType(iRC) = vbLong) or (VarType(iRC) = vbBoolean) then

			' iRC is either a Variant Integer (Either SUCCESS or FAILURE), or a Variant Bool (Either True or False)
			
			If ( (iRC = Success or iRC = 3010) and ((VarType(iRC) = vbInteger) or (VarType(iRC) = vbLong)) ) or ( iRC = TRUE and VarType(iRC) = vbBoolean ) then
				CreateEntry oEnvironment.Substitute( "SUCCESS: " & FormatError(iRC) & ": " & sMessage ), LogTypeVerbose
			ElseIf bFatal then
				' Fatial Error
				ReportFailure  FormatError(iRC) & ": " & sMessage, iError
			Else
				CreateEntry oEnvironment.Substitute( "FAILURE: " & FormatError(iRC) & ": " & sMessage ), LogTypeWarning
			End if
			
		Elseif (VarType(iRC) <> vbEmpty) and (VarType(iRC) <> vbNull ) then

			CreateEntry oEnvironment.Substitute( "UNKNOWN: " & TypeName(iRc) & " = " & iRc & " : " & FormatError(iError) & ": " & sMessage ), LogTypeWarning

		End if

	End function


	Function ProcessResults( iRc )
		Dim iMainRc
		Dim sError
		Dim sMainRc

		iMainRc = iRc
		sMainRc = FormatError ( iMainRc )
		If Err then
			iMainRc = Err.Number
			sError = Err.Description
			sMainRc = FormatError ( iMainRc )
			CreateEvent 41002, LogTypeError, "ZTI ERROR - Unhandled error returned by " & oUtility.ScriptName & ": " & sError & " (" & sMainRc & ")", Array(iMainRc)
		ElseIf iRc <> Success then
			CreateEvent 41002, LogTypeError, "ZTI ERROR - Non-zero return code by " & oUtility.ScriptName & ", rc = " & sMainRc, Array(iMainRc)
		Else
			CreateEvent 41001, LogTypeInfo, oUtility.ScriptName & " processing completed successfully.", Array()
		End if
		WScript.Quit iMainRc

	End function

	Function FormatError( iError )

		' Error messages above 0x1000000 are most likely Hex values, print both Hex and Decimal
		If not isNumeric( iError ) then
			FormatError = ""
		ElseIf Abs(iError) >= &H1000000 then
			FormatError = cstr(iError) & "  0x" & right( "00000000" & hex ( iError ), 8 )
		Else
			FormatError = cstr(iError)
		End if

	End function


End Class

' //////////////////////////////////////////////////////




Class Environment

	' ***  Public variables ***

	Public PersistFile


	' ***  Private variables ***

	Private oVariables
	Private dLastModified
	Private dLastSize
	Private osdV4


	' ***  Constructor and destructor ***

	Private Sub Class_Initialize

		PersistFile = "VARIABLES.DAT"


		On Error Resume Next
		Err.Clear
		Set oVariables = oUtility.CreateXMLDOMObject
		If Err then
			' Unable to create XML object
			Err.Clear
		End if
		On Error Goto 0
		dLastModified = 0
		dLastSize = 0


		' Create SMSv4 Task Sequence environment

		On Error Resume Next
		Err.Clear
		Set osdV4 = CreateObject("Microsoft.SMS.TSEnvironment")
		If Err then
			Set osdV4 = Nothing
			Err.Clear
		End if
		On Error Goto 0

		Err.Clear

	End Sub


	' ***  Private methods ***

	Function GetOSDV4(sVariable)

		GetOSDV4 = ""
		On Error Resume Next
		If osdV4 is Nothing then
			Exit Function
		Else
			GetOSDV4 = osdV4(sVariable)
			If Err then
				' oLogging.CreateEntry "WARNING - Unable to get SMSv4 Task Sequencer environment: " & Err.Description & " (" & Err.Number & ")", LogTypeWarning
			End if
		End if
		On Error Goto 0
		Err.Clear

	End Function

	Function SetOSDV4(sVariable, sNew)
		On Error Resume Next
		If osdV4 is Nothing then
			SetOSDV4 = False
			Exit Function
		Else
			osdV4(sVariable) = sNew
			If Err then
				' oLogging.CreateEntry "WARNING - Unable to get SMSv4 Task Sequencer environment: " & Err.Description & " (" & Err.Number & ")", LogTypeWarning
				SetOSDV4 = False
			End if
		End if
		On Error Goto 0
		Err.Clear
		SetOSDV4 = True

	End Function

	Property Get VariablesDat
		Dim sVariablesFile

		' If necessary, load the XML file

		sVariablesFile = PersistPath & "\" & PersistFile
		If oFSO.FileExists(sVariablesFile) then
			If (oFSO.GetFile(sVariablesFile).DateLastModified <> dLastModified) or (oFSO.GetFile(sVariablesFile).Size <> dLastSize) then
				On Error Resume Next
				Do
					oVariables.Load sVariablesFile
					dLastModified = oFSO.GetFile(sVariablesFile).DateLastModified
					dLastSize = oFSO.GetFile(sVariablesFile).Size
				Loop While oVariables.DocumentElement is Nothing
				On Error Goto 0
			End if
		ElseIf dLastModified = 0 then
			oVariables.LoadXml "<?xml version=""1.0"" ?><MediaVarList Version=""4.00.5345.0000""></MediaVarList>"
			dLastModified = Now  ' The file hasn't been saved yet, but we don't want to reset this
		End if


		' Set the return value to the XML document

		Set VariablesDat = oVariables

	End Property 

	Function GetDAT(sVariable)
		Dim sLockFile
		Dim oNode
		Dim fLock

		GetDAT = ""

		If Ucase(oUtility.ScriptName) <> "LTICLEANUP" Then
			sLockFile = PersistPath & "\" & PersistFile & ".LOCK"
			On error resume next
			Set fLock = nothing
			Set fLock = oFSO.CreateTextFile(sLockFile, 2, true)
			Do while fLock is nothing
				dLastModified = 0 ' FORCE a rescan.
				' ologging.CreateEntry sLockFile & " is locked by another process, Wait!", LogTypeVerbose
				oUtility.SafeSleep 10
				Set fLock = oFSO.CreateTextFile( sLockFile, 2, true)
			Loop
			On error goto 0
		End if 


		On Error Resume Next
		Set oNode = Nothing
		Set oNode = VariablesDat.DocumentElement.SelectSingleNode("//var[@name='" & UCase(sVariable) & "']")
		On Error Goto 0
		If not (oNode is Nothing) then
			GetDAT = oNode.Text
		End if

	End Function

	Function SetDAT(sVariable, sNew)
		Dim sVariablesFile
		Dim sLockFile
		Dim oNode, oCDATA, oVD
		Dim fLock
		
		If osdv4 is Nothing OR oEnvironment.Item("_SMSTSStandAloneMode") = "true" then
			If oFSO.FolderExists(PersistPath) then

				sVariablesFile = PersistPath & "\" & PersistFile

				If Ucase(oUtility.ScriptName) <> "LTICLEANUP" Then
					sLockFile = PersistPath & "\" & PersistFile & ".LOCK"
					On error resume next
					Set fLock = nothing
					Set fLock = oFSO.CreateTextFile(sLockFile, 2, true)
					Do while fLock is nothing
						dLastModified = 0 ' FORCE a rescan.
						' ologging.CreateEntry sLockFile & " is locked by another process, Wait!", LogTypeVerbose
						oUtility.SafeSleep 10
						Set fLock = oFSO.CreateTextFile(sLockFile, 2, true)
					Loop
					On error goto 0
				End if

				Set oVD = VariablesDat


				' See if the variable is already defined.  If not, append a new node

				Set oNode = oVD.DocumentElement.SelectSingleNode("//var[@name='" & UCase(sVariable) & "']")
				If oNode is Nothing then
					Set oNode = oVD.CreateElement("var")
					oVD.DocumentElement.appendChild oNode
				Else
					If oNode.Text = sNew then
						' oLogging.CreateEntry "Not changing variable " & sVariable & " because the value was not changed.", LogTypeInfo
						SetDAT = True
						Exit Function
					End if
				End if


				' Set the name of the node

				oNode.SetAttribute "name", UCase(sVariable)


				' Set the value of the node

				Set oCDATA = oVD.createCDATASection(sNew)
				If oNode.hasChildNodes then
					oNode.removeChild(oNode.childNodes.item(0))
				End if
				oNode.appendChild(oCDATA)


				' Save the updated XML file

				On Error Resume Next
				oVD.Save sVariablesFile
				dLastModified = oFSO.GetFile(sVariablesFile).DateLastModified
				dLastSize = oFSO.GetFile(sVariablesFile).Size
				If Err then
					oLogging.CreateEntry "WARNING - Unable to persist items to " & sVariablesFile & ": " & Err.Description & " (" & Err.Number & ")", LogTypeWarning
					SetDat = False
					Err.Clear
				End if
				On Error Goto 0
			Else
				SetDat=False
			End if
		Else
			SetDat = False
		End If
		SetDat = True
	End Function


	Function ObfuscateEncode(sVariable, sNew)

		Select Case Ucase(sVariable)
		Case "USERID", "USERPASSWORD", "USERDOMAIN", "DOMAINADMIN", "DOMAINADMINPASSWORD", "DOMAINADMINDOMAIN", _
                   "ADMINPASSWORD", "BDEPIN", "TPMOWNERPASSWORD",  "USERNAME", "USERPASSWORD", "PRODUCTKEY", "OSDJOINACCOUNT",_
                   "SAFEMODEADMINPASSWORD","OSDJOINPASSWORD"
			ObfuscateEncode = oStrings.Base64Encode(sNew)
		Case Else
			ObfuscateEncode = sNew
		End Select

	End Function


	Function ObfuscateDecode(sVariable, sCurrent)

		Select Case Ucase(sVariable)
		Case "USERID", "USERPASSWORD", "USERDOMAIN", "DOMAINADMIN", "DOMAINADMINPASSWORD", "DOMAINADMINDOMAIN", _
                   "ADMINPASSWORD", "BDEPIN", "TPMOWNERPASSWORD",  "USERNAME", "USERPASSWORD", "PRODUCTKEY", "OSDJOINACCOUNT",_
                   "SAFEMODEADMINPASSWORD","OSDJOINPASSWORD"
			ObfuscateDecode = oStrings.Base64Decode(sCurrent)


			' If the variable wasn't a valid base64 string, an empty string will be returned.  Instead of
			' passing that back, return the current value.  (The Base64Decode method should log a warning.)

			If ObfuscateDecode = "" and sCurrent <> "" then
				ObfuscateDecode = sCurrent
			End if

		Case Else
			ObfuscateDecode = sCurrent
		End Select

	End Function


	' ***  Public methods ***

	Public Property Get Exists(sVariable)

		If Item(sVariable) <> "" then
			Exists = True
		Else
			Exists = False
		End if

	End Property


	Public Property Get Item(sVariable)

		Dim sOriginal
		Dim bSync


		' First try TS environment, then (for Lite Touch only) try the XML file
		
		Item = GetOSDV4(sVariable)
		If Item = "" and GetOSDV4("DeploymentMethod") <> "SCCM" then

			' No value retrieved from the task sequence, try the XML file

			Item = GetDat(sVariable)
			If Item <> "" then
				bSync = true
			End if

		End if


		' Decode and sync if not blank

		If Item <> "" then

			' Decode the variable

			sOriginal = Item
			Item = ObfuscateDecode(sVariable, sOriginal)


			' Try to set the value in the task sequence environment (sync)

			If bSync then
				SetOSDV4 sVariable, sOriginal
			End if

		End if

	End Property


	Public Property Let Item(sVariable, sNew)

		Dim sEncoded


		' Encode as required

		sEncoded = ObfuscateEncode(sVariable, sNew)


		' Save to all available environments

		If SetOSDV4(sVariable, sEncoded) or SetDat(sVariable, sEncoded) Then
			oLogging.CreateEntry "Property " & sVariable & " is now = " & sNew, LogTypeInfo
		End If


		' For completeness, set the variable in the process's environment as well

		oEnv(sVariable) = sNew

	End Property


	Public Property Get ListItem(sVariable)

		Dim i
		Dim sPadded

		Set ListItem = CreateObject("Scripting.Dictionary")
		For i = 1 to 999

			sPadded = sVariable & Right("000" & CStr(i), 3)
			If Item(sPadded) <> "" then
				If not ListItem.Exists(Item(sPadded)) then
					ListItem.Add Item(sPadded), ""
				End if
			ElseIf Item(sVariable & CStr(i)) <> "" then
				If not ListItem.Exists(Item(sVariable & CStr(i))) then
					ListItem.Add Item(sVariable & CStr(i)), ""
				End if
			Else
				Exit For  ' Exit on first "not found" entry
			End if

		Next

	End Property

	Public Sub SetListItemEx (sVariable, sNew)

		Dim sElement
		Dim i
		Dim sPadded

		i = 0
		For each sElement in sNew
			i = i + 1
			sPadded = sVariable & Right("000" & CStr(i), 3)
			Item(sPadded) = sElement
		Next


		' Blank out the next in case there was something there

		sPadded = sVariable & Right("000" & CStr(i+1), 3)
		If Exists(sPadded) then
			Item(sPadded) = ""
		End if


		' Blank out the non-list item if it was there

		If exists(sVariable) then
			Item(sVariable) = ""
		End if

	End sub

	Public Property Set ListItem(sVariable, sNew)

		SetListItemEx sVariable, sNew

	End Property

	Public Property Let ListItem(sVariable, sNew)

		SetListItemEx sVariable, sNew

	End Property


	Public Property Get IndirectItem( sVariable )

		IndirectItem = oEnvironment.Item(oEnvironment.Item(sVariable))

	End Property

	Public Property Let IndirectItem( sVariable, sNew )

		If oEnvironment.Item(sVariable) = "" then
			oLogging.CreateEntry "Invalid MDT Indirect Variable pointer: " & sVariable, LogTypeWarning
		Else
			oEnvironment.Item(oEnvironment.Item(sVariable)) = sNew
		End if

	End Property


	Function Substitute(sVal)

		Dim sReplace, iPos, iEnd, sEval

		' Substitute the appropriate values

		iPos = Instr(sVal, "%")
		While iPos > 0

			' Find ending "%"
			iEnd = Instr(iPos+1, sVal, "%")
			If iEnd > 0 then

				sEval = Mid(sVal, iPos+1, iEnd - iPos - 1)

				sReplace = ""
				If oEnvironment.ListItem(sEval).Count > 0 then
					For each sReplace in oEnvironment.ListItem(sEval).Keys
						Exit For  ' Grab the first value
					Next
				ElseIf oEnvironment.Item(sEval) <> "" then
					sReplace = oEnvironment.Item(sEval)
				End if

				If sReplace <> "" then
					If iPos = 1 then
						sVal = CStr(sReplace) & Mid(sVal, iEnd + 1)
					ElseIf iEnd = Len(sVal) then
						sVal = Left(sVal, iPos - 1) & CStr(sReplace)
					Else
						sVal = Left(sVal, iPos - 1) & CStr(sReplace) & Mid(sVal, iEnd + 1)
					End if
					iPos = Instr(sVal, "%")
				Else
					iPos = iEnd
				End if
			Else
				iPos = iEnd
			End if
		WEnd


		' Expand any environment variables

		sVal = oShell.ExpandEnvironmentStrings(sVal)


		' Finally, look for evaluate blocks

		iPos = Instr(sVal, "#")
		While iPos > 0

			' Find ending "#"
			iEnd = Instr(iPos+1, sVal, "#")
			If iEnd > 0 then
			
				sEval = Mid(sVal, iPos+1, iEnd - iPos - 1)
				
				sReplace = empty
				On Error Resume Next
				sReplace = Eval(sEval)
				On Error Goto 0
				
				If not isEmpty(sReplace) then
					If iPos = 1 then
						sVal = sReplace & Mid(sVal, iEnd + 1)
					ElseIf iEnd = Len(sVal) then
						sVal = Left(sVal, iPos - 1) & sReplace
					Else
						sVal = Left(sVal, iPos - 1) & sReplace & Mid(sVal, iEnd + 1)
					End if
					iPos = Instr(sVal, "#")
				Else
					iPos = iEnd
				End if
			Else
				iPos = iEnd
			End if

		WEnd

		Substitute = Trim(sVal)

	End Function


	Public Property Get PersistPath
		Dim oOrigPersistFile
		Dim oNewPersistFile
		PersistPath = oUtility.LogPath

		If oEnv("SystemDrive") = "X:" then
			If oFSO.FileExists("X:\minint\smsosd\osdlogs\variables.dat") Then
				Set oOrigPersistFile = oFSO.GetFile("X:\minint\smsosd\osdlogs\variables.dat")
				If oFSO.FileExists(PersistPath & "\VARIABLES.DAT") Then
					Set oNewPersistFile = oFSO.GetFile(PersistPath & "\VARIABLES.DAT")

					If oOrigPersistFile.Size > oNewPersistFile.Size Then
						oFSO.CopyFile "X:\minint\smsosd\osdlogs\variables.dat", PersistPath & "\VARIABLES.DAT", true
					End if
				Else
					oFSO.CopyFile "X:\minint\smsosd\osdlogs\variables.dat", PersistPath & "\VARIABLES.DAT", true
				End if
			End if

		End if

	End Property


	Public Function Release

		Set osdV4 = Nothing

	End Function

End Class


Class Utility

	' ***  Properties ***

	Public isHTML
	Public isWSH
	Public isMSHTA
	Public isCScript
	Public isWScript
	Public oMSHTA
	Public Arguments

	' ***  Private variables ***

	Dim dicNetworkConnections
	Dim sScriptDir
	Dim oBDDUtility
	Dim oSupportedPlatforms
	Private sLocalRootPath
	Private bCacheLocalRootPath
	Private startTime
	Private lastHeartbeat
	Private iVersionMajor
	Private iVersionMinor
	Private iBuildNumber
	Private iRevision

	' ***  Constructor and destructor ***

	Private Sub Class_Initialize

		Dim re
		Dim arrDrives, i
		Dim oContext, oLocator


		' Initialize the objects

		Set oFSO = CreateObject("Scripting.FileSystemObject")
		Set oShell = CreateObject("WScript.Shell")
		Set oEnv = oShell.Environment("PROCESS")
		Set oNetwork = CreateObject("WScript.Network")

		Set objWMI = Nothing
		On Error Resume Next
		Set oContext = CreateObject("WbemScripting.SWbemNamedValueSet")
		oContext.Add "__ProviderArchitecture", 64
		Set oLocator = CreateObject("Wbemscripting.SWbemLocator")
		Set objWMI = oLocator.ConnectServer("","root\cimv2","","",,,,oContext)
		On Error Goto 0

		Set dicNetworkConnections = CreateObject("Scripting.Dictionary")
		dicNetworkConnections.CompareMode = TextCompare
		Set oBDDUtility = Nothing
		Set oSupportedPlatforms = Nothing

		isHTML = FALSE
		isWSH = FALSE
		isMSHTA = FALSE
		isCScript = FALSE
		isWScript = FALSE
		set oMSHTA = nothing
	
		
		on error resume next
			isHTML = IsObject(window.location)
			isWSH = IsObject(WScript)
		on error goto 0

		If isHTML Then
			isMSHTA = document.all.tags("Application").length > 0
			On error resume next
			set oMSHTA = document.all.tags("Application")(0)
			if window.location.hostname <> "" then
				sScriptDir = oFSO.GetParentFolderName( unescape("\\" & window.location.hostname & window.location.pathname ) )
			else
				sScriptDir = oFSO.GetParentFolderName( unescape(window.location.pathname) )
			end if
			On error goto 0

		ElseIf isWSH Then
			isCScript = ucase(right(WScript.FullName,len("Xscript.exe"))) = "CSCRIPT.EXE"
			isWScript = ucase(right(WScript.FullName,len("Xscript.exe"))) = "WSCRIPT.EXE"
			sScriptDir = oFSO.GetParentFolderName(WScript.ScriptFullName)

		End if


		If Mid(sScriptDir, 2, 2) = ":\" then

			' Look to see if this is a mapped drive

			On Error Resume Next
			Set arrDrives = oNetwork.EnumNetworkDrives
			If Err then
				On Error Goto 0
				oLogging.CreateEntry "ERROR - Network is unavailable: " & Err.Description & " (" & Err.Number & ")", LogTypeError
			Else
				On Error Goto 0
				For i = 0 to arrDrives.Count - 1 Step 2
					If arrDrives.Item(i) = UCase(Left(sScriptDir,2)) then
						If Len(sScriptDir) > 3 then
							sScriptDir = arrDrives.Item(i+1) & Mid(sScriptDir, 3)
						Else
							sScriptDir = arrDrives.Item(i+1)
						End if
						Exit For
					End if
				Next
			End if
			On Error Goto 0

		End if


		' By default, set a flag to cache the local root path

		bCacheLocalRootPath = True


		' Record times for heartbeat events

		startTime = Now
		lastHearbeat = startTime

	End Sub


	' ***  Public methods ***

	Public Sub PrepareEnvironment

		Dim sArg

		Set oLogging = New Logging
		Set oEnvironment = New Environment
		Set oStrings = New Strings
		Set oFileHandling = New FileHandling

		set Arguments = GetArguments


		' Loop through all the parameters and turn them into environment variables.  Enforce debug values.

		On Error Resume Next

		For each sArg in Arguments
			If UCase(sArg) = "DEBUG" then
				If UCase(Arguments(sArg)) = "TRUE" or UCase(Arguments(sArg)) = "FALSE" then
					oLogging.CreateEntry "'debug' parameter was specified.", LogTypeInfo
				Else
					oLogging.CreateEntry "Invalid 'debug' parameter specified: " & Arguments(sArg), LogTypeError
					WScript.Quit Failure
				End if
				oEnvironment.Item(sArg) = UCase(Arguments(sArg))
			Else
				oEnvironment.Item(sArg) = Arguments(sArg)
			End if
		Next

		On Error Goto 0
		Err.Clear


		' Log the version

		oLogging.CreateEntry "Microsoft Deployment Toolkit version: " & Version, LogTypeInfo


		' Log where the SMSTS.LOG can be found

		If oEnvironment.Item("_SMSTSLogPath") <> "" then
			oEnvironment.SetDAT "SMSTSLogPath_Cache", oEnvironment.Item("_SMSTSLogPath")
			oLogging.CreateEntry "The task sequencer log is located at " & oEnvironment.Item("_SMSTSLogPath") & "\SMSTS.LOG.  For task sequence failures, please consult this log.", LogTypeInfo
		End if


		' Set a default for debug (if necessary)

		If oEnvironment.Item("Debug") = "" then
			oEnvironment.Item("Debug") = "FALSE"
		End if


		If UCase(oEnvironment.Item("Debug")) = "TRUE" then
			oLogging.Debug = True
		Else
			oLogging.Debug = False
		End if
		
		If oEnvironment.Item("SLShareDynamicLogging") <> "" then
		
			If oEnvironment.Item("UserID") <> "" and oEnvironment.Item("UserDomain") <> "" and oEnvironment.Item("UserPassword") <> "" then

				If oFSO.FolderExists( LogPath ) then

					oLogging.CreateEntry "Write all logging text to " & oEnvironment.Item("SLShareDynamicLogging") , LogTypeInfo
					ValidateConnection oEnvironment.Item("SLShareDynamicLogging")
					oUtility.VerifyPathExists oEnvironment.Item("SLShareDynamicLogging")
					
					If not oFSO.FolderExists(oEnvironment.Item("SLShareDynamicLogging")) then
						oLogging.CreateEntry "An invalid SLShareDynamicLogging value of " & oEnvironment.Item("SLShareDynamicLogging") & " was specified.", LogTypeWarning
					Else
						oLogging.NetworkLogging = oEnvironment.Item("SLShareDynamicLogging")
					End if
					
				End if
		
			End if
			
		End if
		

	End Sub

	' This function converts the string representation of a version "xx.xx.xx.xx" to int
	' The function captures the version major/minor and build major/minor into Utility class variables. 
	' It can be accessed via properties Utility.VersionMajor and Utility.VersionMinor
	' e.g. 
	'	1. GetMajorMinorVersion("10.0.12.1234") this will capture 
	'		Utility.VersionMajor  = 10 and Utility.VersionMinor = 0 (BuildMajor and BuildMinor are not yet exposed as properties but the function still captures it)
	'	2. GetMajorMinorVersion("10.110")
	'		Utility.VersionMajor  = 10 and Utility.VersionMinor = 110 (no BuildMajor and BuildMinor)
	' in case of invalid string representation of version (e.g. GetMajorMinorVersion("Thisisfun") function will set values to Int.Max (32767)	
	Public Function GetMajorMinorVersion(sVersion) 
		Dim length
		Dim splitArray
		splitArray = Split(sVersion,".")
		length = UBound(splitArray)
		If length >=0 then
			Select case length
			Case 1
				iVersionMinor = CInt(splitArray(1)) 
			
			Case 2
				iVersionMinor = CInt(splitArray(1)) 
				iBuildNumber = CInt(splitArray(2)) 
			Case 3
				iVersionMinor = CInt(splitArray(1)) 
				iBuildNumber = CInt(splitArray(2)) 
				iRevision = CInt(splitArray(3)) 
			Case Else	
				iVersionMinor = 32767
				iBuildNumber = 32767
				iRevision = 32767	
			End select
			iVersionMajor = CInt(splitArray(0))				
		Else
			iVersionMajor = 32767  'Int.Max for VbScript is 32767
			iVersionMinor = 32767
			iBuildNumber = 32767
			iRevision = 32767
		End if
	End Function

        ' This function retrieves MSXML DOM document using MSXML2.DomDocument.6.0 
        ' If it cannot load tries to get from MSXML2.DOMDocument.3.0 
	Public Function GetMSXMLDOMDocument 
                On Error Resume Next
                Set GetMSXMLDOMDocument = CreateObject("MSXML2.DOMDocument.6.0")
                If Err Then
                        Set GetMSXMLDOMDocument = CreateObject("MSXML2.DOMDocument")
                End If
                On Error Goto 0
        End Function

	Private Function GetArguments
	
		Dim RegExObj 
		Dim Match 

		set RegExObj = New RegExp
		RegExObj.Global = TRUE
		RegExObj.Multiline = TRUE
		RegExObj.IgnoreCase = TRUE
		RegExObj.Pattern = "\/([^\ ""\:\=]+)(?:(?:[\:\=]""([^""]+)"")|(?:[\:\=]([^\ ""]+)))?"
		
		if isWSH then
			set GetArguments = wscript.arguments.Named

		elseif isMSHTA and (not oMSHTA is nothing) then

			Set GetArguments = CreateObject("Scripting.Dictionary")
			GetArguments.CompareMode = vbTextCompare
			
			for each Match in RegExObj.Execute(oMSHTA.CommandLine)
				If not isempty(Match.submatches(0)) then
					if not GetArguments.Exists(Match.submatches(0)) then
						GetArguments.Add Match.submatches(0), Match.submatches(1) & Match.submatches(2) 
					End if
				End if
			next

		elseif IsHTML then
			Set GetArguments = CreateObject("Scripting.Dictionary")
			GetArguments.CompareMode = vbTextCompare
			
			for each Match in oStrings.ForceAsArray( window.location.search,"&")
				If Instr(1,Match,"=",vbTextCompare) <> 0 then
					If not GetArguments.Exists(left(Match,Instr(1,Match,"=",vbTextCompare)-1)) then
						GetArguments.Add left(Match,Instr(1,Match,"=",vbTextCompare)-1), mid(Match,Instr(1,Match,"=",vbTextCompare)+1)
					End if				
				End if
			next
			
		else 
			Set GetArguments = CreateObject("Scripting.Dictionary")
			GetArguments.CompareMode = vbTextCompare

		end if
	End function


	Function GetPA
		GetPA = Replace(oShell.Environment("PROCESS")("Processor_Architecture"),"amd64","x64",1,-1,vbTextCompare)
	end function


	Public Function RunNewInstanceEx ( sClassName, sClassInstance, sMain )
		Dim oScriptClass
		Dim iScriptRc
		
		' Disable On Error Resume next for advanced debugging
		
		If not oUtility.Arguments.Exists("DebugCapture") then
			On Error Resume next
		End if
		

		execute "Set " & sClassInstance & " = New " & sClassName
		TestAndFail SUCCESS, 5400, "Create object: Set " & sClassInstance & " = New " & sClassName

		If oUtility.Arguments.Exists("TestHook") then

			' Hook for Unit Test code. Allows us to run Unit Tests without modification of the Original script.

			oLogging.CreateEntry "Run Test Script [" & oUtility.Arguments.Item("TestHook") & "]...", LogTypeInfo
			Execute oFSO.OpenTextFile(oUtility.Arguments.Item("TestHook")).ReadAll
			oLogging.CreateEntry "Finished with Test Script! iScriptRc = " & iScriptRc, LogTypeInfo

		End if

		' Standard execution, call the main function in the created class.

		iScriptRc =  eval(sMain) 
		ProcessResults iScriptRc


		Wscript.quit iScriptRc

	end function 
	
	Function ConvertBooleanToString(bValue)

		Dim iRetVal 

		iRetVal = Failure
			
		If bValue = -1 Then
			iRetVal = "True"
		ElseIf bValue = 0 Then
			iRetVal = "False" 
		End If

		ConvertBooleanToString = iRetVal

	End Function


	Sub ResetLocalRootPath
		sLocalRootPath = empty
	End sub

	Property Get CacheLocalRootPath
		CacheLocalRootPath = bCacheLocalRootPath 
	End Property
	
	Property Let CacheLocalRootPath(bNew)
		bCacheLocalRootPath = bNew
	End Property

	Public Property Get VersionMajor
		VersionMajor = iVersionMajor
	End Property
	
	Public Property Get VersionMinor
		VersionMinor = iVersionMinor
	End Property
	
	Public Property Get BuildNumber
		BuildNumber = iBuildNumber
	End Property
	
	Public Property Get RevisionNumber
		RevisionNumber = iRevision
	End Property
	
	Property Get LocalRootPath

		Dim sFallBack

		If bCacheLocalRootPath and (not IsEmpty(sLocalRootPath)) then
			
			LocalRootPath = sLocalRootPath ' Cache the value. Enumeration of Drives can be time expensive.
			Exit property

		End if

		If oEnvironment.GetOSDV4("_SMSTSBootImageID") <> "" then
			LocalRootPath = oEnvironment.GetOSDV4("_SMSTSMDataPath")
		Else
			LocalRootPath = ""

			If oEnv("SystemDrive") = "X:" then
				' We're in PE
				sFallBack = "X:\MININT"
			Else
				' We're in a full OS
				sFallBack = "C:\MININT"
			End if
		End if

		' --------------------------------------------
		If LocalRootPath = "" or IsEmpty(LocalRootPath) then
			For each oDrive in oFSO.Drives
				If oDrive.DriveType = 2 and oDrive.DriveLetter<>"X" then
					If oDrive.IsReady Then

						If OFSO.FolderExists(oDrive.DriveLetter & ":\MININT\SMSOSD\OSDLOGS") then
							LocalRootPath = oDrive.DriveLetter & ":\MININT"
							Exit For
						End if
					
						If oFSO.FolderExists(oDrive.DriveLetter & ":\_SMSTaskSequence") then
							sFallBack = oDrive.DriveLetter & ":\MININT"
						End if
					End If

				End if
			Next
			If LocalRootPath = "" or IsEmpty(LocalRootPath) then
				LocalRootPath = sFallBack
			End if

		End if


		' Make sure the root path exists

		oUtility.VerifyPathExists LocalRootPath


		' Cache the value

		sLocalRootPath = LocalRootPath

	End Property

	Property Get BootDevice
	
		BootDevice = UCase(oShell.Regread("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemBootDevice"))

	End Property


	Property Get LogPath

		If oEnvironment.GetOSDV4("_SMSTSBootImageID") <> "" then
			LogPath = oEnvironment.GetOSDV4("_SMSTSLogPath")
		Else
			LogPath = LocalRootPath & "\SMSOSD\OSDLOGS"
		End if
		oUtility.VerifyPathExistsEx LogPath, False

	End Property


	Property Get StatePath

		Dim sBasePath

		' Always use the root of the drive.

		sBasePath = "\"


		' We want to store user state on the same volume as the OS.

		If oEnv("SystemDrive") <> "X:" then
			StatePath = oEnv("SystemDrive") & sBasePath & "StateStore"
		Else
			' We're offline, so try to use OSDWinPEWinDir, which should be set before reaching here

			If oFSO.FolderExists(oEnvironment.Item("OSDWinPEWinDir")) then

				' Use the drive that OSDWinPEWinDir points to

				StatePath = Left(oEnvironment.Item("OSDWinPEWinDir"), 2) & sBasePath & "StateStore"

			Else
				' Fallback logic - put the folder on the local root path

				StatePath = Left(LocalRootPath, 2) & sBasePath & "StateStore"

			End if
		End if

	End Property


	Property Get ScriptName

		On Error Resume Next
		ScriptName = oFSO.GetBaseName(Wscript.ScriptName)
		If Err then
			ScriptName = oFSO.GetBaseName(Unescape(window.location.pathname))
		End if
		On Error Goto 0

	End Property


	Property Get ScriptDir

		ScriptDir = sScriptDir

	End Property


	Public Function ReadIni(file, section, item)

		Dim line, equalpos, leftstring, ini

		ReadIni = ""
		file = Trim(file)
		item = Trim(item)

		On Error Resume Next
		Set ini = oFSO.OpenTextFile( file, 1, False)
		If Err then
			Err.Clear
			Exit Function
		End if
		On Error Goto 0

		Do While (not ini.AtEndOfStream)
			line = ini.ReadLine
			line = oStrings.TrimAllWS(line)
			If LCase(line) = "[" & LCase(section) & "]" and (not ini.AtEndOfStream) Then
				line = ini.ReadLine
				line = oStrings.TrimAllWS(line)
				Do While Left( line, 1) <> "["
					'If InStr( 1, line, item & "=", 1) = 1 Then
					equalpos = InStr(1, line, "=", 1 )
					If equalpos > 0 Then
						leftstring = Left(line, equalpos - 1 )
						leftstring = oStrings.TrimAllWS(leftstring)
						If LCase(leftstring) = LCase(item) Then
							ReadIni = Mid( line, equalpos + 1 )
							ReadIni = oStrings.TrimAllWS(ReadIni)
							Exit Do
						End If
					End If

					If ini.AtEndOfStream Then Exit Do
					line = ini.ReadLine
					line = oStrings.TrimAllWS(line)
				Loop
				Exit Do
			End If
		Loop
		ini.Close

	End Function

	Public Sub WriteIni( file, section, item, myvalue )

		Dim in_section, section_exists, item_exists, wrote, itemtrimmed
		Dim read_ini, write_ini, temp_ini, linetrimmed, line, equalpos
		Dim leftstring

		in_section = False
		section_exists = False
		item_exists = ( ReadIni( file, section, item ) <> "" )
		wrote = False
		file = Trim(file)
		itemtrimmed = Trim(item)
		myvalue = Trim(myvalue)

		temp_ini = oFSO.GetParentFolderName(file) & "\" & oFSO.GetTempName

		Set read_ini = oFSO.OpenTextFile( file, 1, True, False )
		Set write_ini = oFSO.CreateTextFile( temp_ini, False)

		While read_ini.AtEndOfStream = False
			line = read_ini.ReadLine
			linetrimmed = Trim(line)
			If wrote = False Then
				If LCase(line) = "[" & LCase(section) & "]" Then
					section_exists = True
					in_section = True
				ElseIf InStr( line, "[" ) = 1 Then
					in_section = False
				End If
			End If

			If in_section Then
				If itemtrimmed = "" then
					' Do nothing: we want to wipe the section
				ElseIf item_exists = False Then
					write_ini.WriteLine line
					If myvalue <> "" then
						write_ini.WriteLine item & "=" & myvalue
					End if
					wrote = True
					in_section = False
				Else
					equalpos = InStr(1, line, "=", 1 )
					If equalpos > 0 Then
						leftstring = Left(line, equalpos - 1 )
						leftstring = Trim(leftstring)
						If LCase(leftstring) = LCase(item) Then
							If myvalue <> "" then
								write_ini.WriteLine itemtrimmed & "=" & myvalue
							End if
							wrote = True
							in_section = False
						End If
					End If
					If Not wrote Then
						write_ini.WriteLine line
					End If
				End If
			Else
				write_ini.WriteLine line
			End If
		Wend

		If section_exists = False and itemtrimmed <> "" Then ' section doesn't exist
			write_ini.WriteLine
			write_ini.WriteLine "[" & section & "]"
			If myvalue <> "" then
				write_ini.WriteLine itemtrimmed & "=" & myvalue
			End if
		End If

		read_ini.Close
		write_ini.Close
		If oFSO.FileExists(file) then
			oFSO.DeleteFile file, True
		End if
		oFSO.CopyFile temp_ini, file, true
		oFSO.DeleteFile temp_ini, True

	End Sub

	Public Function Sections(file)
		Dim oContents
		Dim line, equalpos, leftstring, ini

		Set oContents = CreateObject("Scripting.Dictionary")
		file = Trim(file)

		On Error Resume Next
		Set ini = oFSO.OpenTextFile( file, 1, False)
		If Err then
			Err.Clear
			Exit Function
		End if
		On Error Goto 0

		Do While ini.AtEndOfStream = False
			line = ini.ReadLine
			line = Trim(line)
			If Left(line,1) = "[" then
				equalpos = Instr(line, "]")
				leftstring = Mid(line, 2, equalpos - 2)
				oContents.Add leftstring, ""
			End if
		Loop

		Set Sections = oContents

	End Function

	Public Function SectionContents(file, section)
		Dim oContents
		Dim line, equalpos, leftstring, ini

		Set oContents = CreateObject("Scripting.Dictionary")
		file = Trim(file)
		section = Trim(section)

		On Error Resume Next
		Set ini = oFSO.OpenTextFile( file, 1, False)
		If Err then
			Err.Clear
			Exit Function
		End if
		On Error Goto 0

		Do While ini.AtEndOfStream = False
			line = ini.ReadLine
			line = Trim(line)
			If LCase(line) = "[" & LCase(section) & "]" Then
				line = ini.ReadLine
				line = Trim(line)
				Do While Left( line, 1) <> "["
					'If InStr( 1, line, item & "=", 1) = 1 Then
					equalpos = InStr(1, line, "=", 1 )
					If equalpos > 0 Then
						leftstring = Left(line, equalpos - 1 )
						leftstring = Trim(leftstring)
						oContents(leftstring) = Trim(Mid(line, equalpos + 1 ))
					End If

					If ini.AtEndOfStream Then Exit Do
					line = ini.ReadLine
					line = Trim(line)
				Loop
				Exit Do
			End If
		Loop
		ini.Close
		Set SectionContents = oContents

	End Function


	Function RegReadEx ( sRegValue, bError )

		on error resume next
		RegReadEx = oShell.Regread( sRegValue )
		If bError then
			TestAndLog not isempty(RegReadEx),  "ReadRead(" & sRegValue & ")"
		End if
		on error goto 0

	End function

	Function RegWriteEx ( sKey, sValue, bError )

		on error resume next
		if VarType(sValue) = vbString then
			RegWriteEx = oShell.RegWrite ( sKey, sValue, "REG_SZ"  )
		elseif VarType(sValue) = vbInteger or VarType(sValue) = vbLong or VarType(sValue) = vbBoolean then 
			RegWriteEx = oShell.RegWrite ( sKey, sValue, "REG_DWORD" )
		end if 

		If bError then
			TestAndLog (err = 0), "RegWriteEx( " & sKey & " , " & sValue & " , REG_Type )"
		End if
		on error goto 0

	End function


	Function RegRead( sRegValue )
		RegRead = RegReadEx(sRegValue, True)
	end Function 

	Function RegWrite ( sKey, sValue )
		RegWrite = RegWriteEx(sKey, sValue, True)
	end Function
	
	Function SafeSleep ( iTime )
		On error resume next
		WScript.Sleep iTime
		On error goto 0
	End function 

	' ---------------------------------------------------------------

	Function RunCommandLog( oExec, fExternalHook, iLimit )

		Dim HeartbeatStart
		Dim iMinutes
		Dim iHeartBeat
		Dim iLastProgress
		Dim iResult

		' Initialize the last heartbeat time (start the timer) and interval

		HeartbeatStart = Now
		iLastProgress = 0
		iResult = SUCCESS

		' Main Processing Loop
		do while oExec.Status = 0

			' See if it is time for a heartbeat
			iMinutes = DateDiff("n", HeartbeatStart, now )
			If iMinutes >= iLimit and iLimit <> -1 and VarType(iLimit) = 2 then
				oLogging.CreateEvent 41009, LogTypeError, "Command has been running for " & CStr(iMinutes) & " minutes (process ID " & oExec.ProcessID & "). Now over limit!", Array(iMinutes)
				RunCommandLog = False
				exit function
			End if
			Heartbeat "ZTI Heartbeat: command has been running for " & CStr(iMinutes) & " minutes (process ID " & oExec.ProcessID & ")"

			If IsNull(fExternalHook) then
				SafeSleep 100
			Else
				SafeSleep 1
				If VarType(fExternalHook) = 9 then
					iResult = fExternalHook(oExec, iLastProgress)  ' fExternal Hook is a GetRef( function )
				ElseIf VarType(fExternalHook) = 8 then
					iResult = execute( fExternalHook )  ' fExternalHook is a string function
				Else
					iResult = StandardConsoleProcessing(oExec, iLastProgress) ' Do standard processing
				End if

				If iResult <> SUCCESS then
				 	Exit do
				End if
			End if

		loop 


		If IsNull(fExternalHook)  or iResult <> SUCCESS then
			SafeSleep 100
		Else
			' Jump to the end of the sub-progress if started
			If not IsEmpty(iLastProgress) and iLastProgress > 9 and iLastProgress < 91 then
				oLogging.ReportProgress "process ID " & oExec.ProcessID & " Finished ", 100
			End if

			If VarType(fExternalHook) = 9 then
				iResult = fExternalHook(oExec, iLastProgress)  ' fExternal Hook is a GetRef( function )
			ElseIf VarType(fExternalHook) = 8 then
				iResult = execute( fExternalHook )  ' fExternalHook is a string function
			Else
				iResult = StandardConsoleProcessing(oExec, iLastProgress) ' Do standard processing
			End if

		End if

		' Return the exit code to the caller
		oLogging.CreateEntry "Return code from command = " & oExec.ExitCode, LogTypeInfo
		RunCommandLog = oExec.ExitCode

	End function


	Function Heartbeat(sMessage)
		Dim iMinutes

		If DateDiff("n", lastHeartbeat, Now ) >= 5 then
			lastHeartbeat = Now
			iMinutes = DateDiff("n", startTime, lastHeartbeat)
			oLogging.CreateEvent 41003, LogTypeInfo, sMessage, Array(iMinutes)
		End if
	End function


	Function  StandardConsoleProcessing ( oExec, byRef iLastProgress ) 
		Dim sLines
		Dim sLine
		Dim oMatch

		StandardConsoleProcessing = SUCCESS

		Do Until oExec.StdOut.atEndOfStream

			sLines = oExec.StdOut.ReadLine
			if vartype(sLines) = 8 then
				For each sLine in split(sLines,vbNewLine)
					If asc(mid(sline,1,1)) = 0 then
						sLine = replace(sLine,chr(0),"") ' Quick and dirty Unicode to ANSI fix.
					End if
					oLogging.CreateEntry "  Console > " & sLine, LogTypeInfo

					if oExec.Status = 0 then
						' If the output contains a percentage, then interpret it a status.
						
						' Check for DISM-style progress
						Set oMatch = oRegEx.GetRegExMatches("([01]?[0-9]?[0-9])\.[0-9]\%",sLine)
						If oMatch.Count > 0 then
							iLastProgress = cint(oMatch(oMatch.Count -1).SubMatches(0))
							oLogging.ReportProgress sLine, iLastProgress
						Else
							' Check for "other" percentage
							set oMatch = oRegEx.GetRegExMatches("([01]?[0-9]?[0-9]) ?\%",sLine)
							If oMatch.Count > 0 then
								iLastProgress = cint(oMatch(oMatch.Count -1).SubMatches(0))
								oLogging.ReportProgress sLine, iLastProgress
							End if
						End if
					End if
				Next
			End if
			if oExec.Status = 0 then
				Exit Function
			End if

		Loop

		if oExec.Status <> 0 then
			StandardConsoleProcessing = FAILURE
		Else

			do until oExec.StdErr.atEndOfStream
				sLines = oExec.StdErr.ReadAll
				if vartype(sLine) = 8 then
					For each sLine in split(sLines,vbNewLine)
						If asc(mid(sline,1,1)) = 0 then
							sLine = replace(sLine,chr(0),"") ' Quick and dirty Unicode to ANSI fix.
						End if
						oLogging.CreateEntry "  Console # " & sLine, LogTypeError
					Next
				End if
			loop

		End if

	End Function

	Function StandardFileProcessing ( oExec, ByRef iLastProgress, sExternalLogFile )
		Dim oMatch

		If oExec.Status = 0 then
			If oFSO.FileExists(sExternalLogFile) then
				set oMatch = oRegEx.GetRegExMatchesFromFile(sProgress,sExternalLogFile)
				If oMatch.Count > 0 then
					If cint(oMatch(oMatch.Count - 1).SubMatches(0)) > iLastProgress then
						iLastProgress = cint(oMatch(oMatch.Count - 1).SubMatches(0))
						oLogging.ReportProgress "Progress [" & sExternalLogFile & "] ...  " & iLastProgress & "%", iLastProgress
						exit function 
					End if
				End if
			End if
		End if
		oUtility.SafeSleep 100

	End function


	Function RunCommandStart(sCmd, aInput)

		Dim oExec
		Dim sLine

		' Start the command

		oLogging.CreateEntry "About to run command: " & sCmd, LogTypeInfo
		Set oExec = oShell.Exec(sCmd)

		oLogging.CreateEntry "Command has been started (process ID " & oExec.ProcessID & ")", LogTypeInfo

		' Log any input lines

		If isArray( aInput )  then
			For each sLine in aInput
				RunCommandWrite oExec, sLine
			next
		End if

		set RunCommandStart = oExec

	End function


	Function RunCommandWrite( oExec, sCommand ) 

		If sCommand <> "" then
			oLogging.CreateEntry  "  Console + " & sCommand, LogTypeInfo
			oExec.stdIn.WriteLine sCommand
		End if

	End function


	Function RunCommandEx(sCmd, aInput, fExternalHook, iLimit )

		Dim oExec

		' Start the command
		set oExec = RunCommandStart(sCmd, aInput)
		SafeSleep 500

		' Wait for the results
		RunCommandEx = RunCommandLog (oExec, fExternalHook, iLimit )

	End function

	Function RunWithHeartbeat(sCmd)
			RunWithHeartbeat = RunCommandEx(sCmd, null, null, -1)
	End function


	Function RunWithConsoleLogging(sCmd)
		RunWithConsoleLogging = RunCommandEx(sCmd, empty, empty, -1)
	End function

	Function RunWithConsoleLoggingAndHidden(sCmd)
		Dim iRC
		Dim sScriptFile

		iRC = oUtility.FindFile( "ztiRunCommandHidden.wsf", sScriptFile )
		TestAndLog iRC, "Verify ztiRunCommandHidden.wsf is in the path"
		If iRC = Success then
			RunWithConsoleLoggingAndHidden = RunCommand ( "cscript.exe //nologo """ & sScriptFile & """ """ & sCmd & """", 0, True)
		Else
			oLogging.CreateEntry "Visible console fallback." , LogTypeInfo
			RunWithConsoleLoggingAndHidden = oUtility.RunWithConsoleLogging( sCmd )
		End if
	End function

	Function FindExeAndRunWithLogging( sCmd, sParameters )

		Dim iRC
		Dim sFoundFile

		iRC = oUtility.FindFile ( sCmd, sFoundFile )
		TestAndFail iRC, 5441, "FindFile: " & sCmd

		FindExeAndRunWithLogging = RunWithConsoleLogging( """" & sFoundFile & """ " & sParameters )

	End function

	Function FindExeAndRun( sCmd, sParameters )

		Dim iRC
		Dim sFoundFile

		iRC = oUtility.FindFile ( sCmd, sFoundFile )
		TestAndFail iRC, 5442, "FindFile: " & sCmd

		FindExeAndRun = RunWithHeartbeat( """" & sFoundFile & """ " & sParameters )

	End function


	Function RunCommand ( sCmd, iWindowStyle, bWaitOnReturn )
		oLogging.CreateEntry "About to run command: " & sCmd, LogTypeInfo
		RunCommand = oShell.Run ( sCmd, iWindowStyle, bWaitOnReturn )
		oLogging.CreateEntry "Command has returned: " & RunCommand, LogTypeInfo
	End Function



	Function ComputerName

		Dim re
		Dim sComputer


		Set re = New RegExp


		' Figure out the computer name to include

		If oEnvironment.Item("_SMSTSMachineName") <> "" then
			If oEnvironment.Item("OSDCOMPUTERNAME") <> "" and Instr(oEnvironment.Item("OSDCOMPUTERNAME"),":") = 0 then
				sComputer = oEnvironment.Item("OSDComputerName")
			Else
				sComputer = oEnvironment.Item("_SMSTSMachineName")
			End If
		ElseIf oEnvironment.Item("OSDCOMPUTERNAME") <> "" and Instr(oEnvironment.Item("OSDCOMPUTERNAME"),":") = 0 then
			sComputer = oEnvironment.Item("OSDCOMPUTERNAME")
		ElseIf oEnvironment.Item("OSDNEWMACHINENAME") <> "" then
			oEnvironment.Item("OSDComputerName") = oEnvironment.Item("OSDNEWMACHINENAME")
			sComputer = oEnvironment.Item("OSDCOMPUTERNAME")
		ElseIf oEnvironment.Item("ComputerName") <> "" then
			oEnvironment.Item("OSDComputerName") = oEnvironment.Item("ComputerName")
			sComputer = oEnvironment.Item("OSDComputerName")
		ElseIf oEnvironment.Item("OSVersion") = "WinPE" and oEnvironment.Item("OSDCOMPUTERNAME") <> "" then
			sComputer = oEnvironment.Item("OSDCOMPUTERNAME")
			re.Pattern = ":"
			sComputer = re.Replace(sComputer, "")
		Else
			sComputer = oEnvironment.Item("HostName")
		End if

		ComputerName = oEnvironment.Substitute(sComputer)

	End Function
	
	
	Function FindFile(sFilename, sFoundPath)

		Dim iRetVal
		Dim sDir


		iRetVal = Failure
		sFoundPath = ""


		' Look through the standard locations

		If oEnvironment.Item("DeployRoot") <> "" then

			For each sDir in Array("\", "\Tools\", "\Servicing\", "\Scripts\", "\Control\", "\Templates\", "\x86\", "\amd64\")

				If oFSO.FileExists(oEnvironment.Item("DeployRoot") & sDir & sFileName) then
					sFoundPath = oEnvironment.Item("DeployRoot") & sDir & sFileName
					iRetVal = Success
					Exit For
				ElseIf oEnvironment.Item("Architecture") <> "" and oFSO.FileExists(oEnvironment.Item("DeployRoot") & sDir & oEnvironment.Item("Architecture") & "\" & sFileName) then
					sFoundPath = oEnvironment.Item("DeployRoot") & sDir & oEnvironment.Item("Architecture") & "\" & sFileName
					iRetVal = Success
					Exit For

				' oEnvironment.Item("Architecture") *can* be blank, search in the %Processor_Architecture% directory.
				ElseIf ucase(oEnv("Processor_Architecture")) = "AMD64" and oFSO.FileExists(oEnvironment.Item("DeployRoot") & sDir & "x64\" & sFileName) then
					sFoundPath = oEnvironment.Item("DeployRoot") & sDir & "x64\" & sFileName
					iRetVal = Success
					Exit For
				ElseIf oFSO.FileExists(oEnvironment.Item("DeployRoot") & sDir & oEnv("Processor_Architecture") & "\" & sFileName) then
					sFoundPath = oEnvironment.Item("DeployRoot") & sDir & oEnv("Processor_Architecture") & "\" & sFileName
					iRetVal = Success
					Exit For
				End if

			Next

		End if
		If sFoundPath <> "" then
			FindFile = iRetVal
			Exit Function
		End if


		' Check resource root locations (normally OSD only)

		If oEnvironment.Item("ResourceRoot") <> "" and oEnvironment.Item("ResourceRoot") <> oEnvironment.Item("DeployRoot") then

			For each sDir in Array("\", "\Tools\", "\Servicing\", "\Scripts\", "\Templates\", "\Control\")

				If oFSO.FileExists(oEnvironment.Item("ResourceRoot") & sDir & sFileName) then
					sFoundPath = oEnvironment.Item("ResourceRoot") & sDir & sFileName
					iRetVal = Success
					Exit For
				ElseIf oFSO.FileExists(oEnvironment.Item("ResourceRoot") & sDir & oEnvironment.Item("Architecture") & "\" & sFileName) then
					sFoundPath = oEnvironment.Item("ResourceRoot") & sDir & oEnvironment.Item("Architecture") & "\" & sFileName
					iRetVal = Success
					Exit For
				End if

			Next

		End if
		If sFoundPath <> "" then
			FindFile = iRetVal
			Exit Function
		End if


		' Now look in the log directory, this directory, working directory, and SYSTEM32 directory

		If oFSO.FileExists(oUtility.LogPath & "\" & sFilename) then
			sFoundPath = oUtility.LogPath & "\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(oUtility.ScriptDir & "\" & sFilename) then
			sFoundPath = oUtility.ScriptDir & "\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(oUtility.LocalRootPath & "\" & sFileName) then
			sFoundPath = oUtility.LocalRootPath & "\" & sFileName
			iRetVal = Success
		ElseIf oFSO.FileExists(".\" & sFileName) then
			sFoundPath = oShell.CurrentDirectory & "\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(oEnvironment.Substitute("%WINDIR%\SYSNATIVE\" & sFilename)) then
			sFoundPath = oEnvironment.Substitute("%WINDIR%\SYSNATIVE\" & sFilename)
			iRetVal = Success
		ElseIf oFSO.FileExists(oEnvironment.Substitute("%WINDIR%\SYSTEM32\" & sFilename)) then
			sFoundPath = oEnvironment.Substitute("%WINDIR%\SYSTEM32\" & sFilename)
			iRetVal = Success
		ElseIf oEnvironment.Item("OSDPACKAGEPATH") <> "" and oFSO.FileExists(oEnvironment.Item("OSDPACKAGEPATH") & "\" & sFilename) then
			sFoundPath = oEnvironment.Item("OSDPACKAGEPATH") & "\" & sFilename
			iRetVal = Success
		ElseIf oEnvironment.Item("_SMSTSPackagePath") <> "" and oFSO.FileExists(oEnvironment.Item("_SMSTSPackagePath") & "\" & sFilename) then
			sFoundPath = oEnvironment.Item("_SMSTSPackagePath") & "\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(".\x86\" & sFileName) then
			sFoundPath = oShell.CurrentDirectory & "\x86\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(".\x64\" & sFileName) then
			sFoundPath = oShell.CurrentDirectory & "\x64\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(".\amd64\" & sFileName) then
			sFoundPath = oShell.CurrentDirectory & "\amd64\" & sFilename
			iRetVal = Success
		ElseIf oFSO.FileExists(sFileName) then
			sFoundPath = sFilename
			iRetVal = Success
		End if

		If iRetVal <> Success then
			oLogging.CreateEntry "FindFile: The file " & sFilename & " could not be found in any standard locations.", LogTypeInfo
		End if

		FindFile = iRetVal

	End Function


	Function FindMappedDrive(sServerUNC)

		Dim arrSplit
		Dim sServerShare, sServerName
		Dim arrDrives, i


		' If the UNC isn't a UNC, just return the drive letter

		If Len(sServerUNC) < 3 then
			FindMappedDrive = ""
			EXIT FUNCTION
		End if
		If Mid(sServerUNC,2,2) = ":\" then
			FindMappedDrive = Left(sServerUNC, 2)
			EXIT FUNCTION
		End if


		' Build the UNC

		If Instr(Mid(sServerUNC, 3), "\") <= 0 or Left(sServerUNC, 2) <> "\\" then
			FindMappedDrive = ""
			EXIT FUNCTION
		End if
		arrSplit = Split(Mid(sServerUNC,3), "\",2)
		sServerName = arrSplit(0)
		sServerShare = "\\" & sServerName & "\" & arrSplit(1)


		' Look to see if this is a mapped drive

		On Error Resume Next
		Set arrDrives = oNetwork.EnumNetworkDrives
		If Err then
			FindMappedDrive = ""
			Exit Function
		Else
			On Error Goto 0
			For i = 0 to arrDrives.Count - 1 Step 2
				If UCase(sServerShare) = UCase(arrDrives.Item(i+1)) and arrDrives.Item(i) <> "" then
					FindMappedDrive = arrDrives.Item(i)
					Exit Function
				End if
			Next
		End if
		On Error Goto 0

		FindMappedDrive = ""

	End Function


	Function ValidateConnection(sServerUNC)
		ValidateConnection = ValidateConnectionEx(sServerUNC, False)
	End function
	
	Function GetNextNAACred(Index)
		Dim iMaxNumLength
		Dim sNumberAsString
		Dim sNAAName
		Dim sNAAPass
				
		iMaxNumLength = 3
		sNAAName = "_SMSTSReserved1-"
		sNAAPass = "_SMSTSReserved2-"
		
		If Index <= 0 Then
			sNumberAsString = "000"
		Else
			sNumberAsString = String(iMaxNumLength - Fix(Log(Fix(Index)) / Log(10)) - 1, "0") & CStr(Fix(Index))
		End If
		
		sNAAName = sNAAName & sNumberAsString
		sNAAPass = sNAAPass & sNumberAsString
		
		If oEnvironment.Item(sNAAName) <> "" AND oEnvironment.Item(sNAAPass) <> "" Then
			OEnvironment.Item("UserDomain") = Left(oEnvironment.Item(sNAAName),Instr(oEnvironment.Item(sNAAName),"\")-1)
			oEnvironment.Item("UserID")= Mid(oEnvironment.Item(sNAAName),Instr(oEnvironment.Item(sNAAName),"\")+1)
			oEnvironment.Item("UserPassword")=oEnvironment.Item(sNAAPass)
			
			GetNextNAACred = True
		Else
			GetNextNAACred = False
		End if
	End Function
	
	Function ValidateConnectionEx(sServerUNC, bForceConnection)

		Dim iRetVal
		Dim arrSplit
		Dim sServerShare, sServerName, sFoundDrive
		Dim sOSDConnectToUNC, sRIPInfo, sCmd
		Dim sWizardHTA, sUserID
		Dim sCurrentServerName, i

		' Make sure a UNC is specified

		If sServerUNC = "" then
			oLogging.CreateEntry "WARNING - Unable to validation connection because a blank UNC was specified.", LogTypeWarning
			ValidateConnectionEx = Failure
			EXIT FUNCTION
		End if
		If Mid(sServerUNC,2,2) = ":\" then
			oLogging.CreateEntry "Using a local or mapped drive, no connection is required.", LogTypeInfo
			ValidateConnectionEx = Success
			EXIT FUNCTION
		End if

		oLogging.CreateEntry "Validating connection to " & sServerUNC, LogTypeInfo
		iRetVal = ValidateNetworkConnectivity
		If iRetVal <> Success then

			'blow up
			ValidateConnectionEx = Failure
			Exit Function
		End IF


		' See if we've already connected

		If bForceConnection then
			' When forcing a connection, map all the way down to the specified folder
			arrSplit = Split(Mid(sServerUNC,3), "\", 2)
		Else
			' When not forcing, split into more chunks so we only get server and share
			arrSplit = Split(Mid(sServerUNC,3), "\")
		End if
		sServerName = arrSplit(0)
		sServerShare = "\\" & sServerName & "\" & arrSplit(1)

		oLogging.CreateEntry "Mapping server share: " & sServerShare, LogTypeInfo
		
		' This isn't necessary if we're trying to connect to the current DP, so check that.
		If Left(oUtility.ScriptDir, 2) = "\\" then

			arrSplit = Split(Mid(oUtility.ScriptDir, 3), "\")
			sCurrentServerName = arrSplit(0)
			If UCase(sServerName) = UCase(sCurrentServerName) then

				oLogging.CreateEntry "Already connected to server " & sServerName & " as that is where this script is running from.", LogTypeInfo
				If bForceConnection then

					' We want a mapped drive in this case, without credentials because we are already connected

					If MapNetworkDrive(sServerShare, "", "") <> Success then
						oLogging.CreateEntry "Unable to map a drive to the deployment share.", LogTypeInfo
					End if

				End if

				ValidateConnectionEx = Success
				EXIT FUNCTION

			End if

		End if



		' Now see if there is already a server connection.
		' We may wish to use bForceConnection if we *Require* the Drive letter, 
		'   even though the computer may be connected to the sever via another share.
		If dicNetworkConnections.Exists(sServerName) and not bForceConnection then
			oLogging.CreateEntry "Already connected to server " & sServerName, LogTypeInfo
			ValidateConnectionEx = Success
			EXIT FUNCTION
		End if


		'Map the SCCM user variables to the CS.INI variables
		GetNextNAACred 0 'Attempt to get first NAA info

		'  It is possible that the server allows anonymous connections, skip if the share is readable.
		'if oFso.FolderExists(sServerName) then
		'	oLogging.CreateEntry "Already connected to server " & sServerName, LogTypeInfo
		'	ValidateConnectionEx = Success
		'	EXIT FUNCTION
		'end if

		' If no credentials are available, prompt. (Only works in WinPE)

		If oEnvironment.Item("UserID") = "" or oEnvironment.Item("UserPassword") = "" or (  oEnvironment.Item("UserDomain") = "" and _
			instr(1,oEnvironment.Item("UserID"),"\",vbTextCompare) = 0 and instr(1,oEnvironment.Item("UserID"),"@",vbTextCompare) = 0 ) then

			' Find the HTA that prompts for credentials

			iRetVal = FindFile("Wizard.hta", sWizardHTA)
			If iRetVal <> Success then
				oLogging.CreateEntry "ERROR - Unable to find Wizard.hta, so it is impossible to prompt for credentials.", LogTypeError
				ValidateConnectionEx = Failure
				Exit Function
			End if

			' Execute the HTA

			oShell.Run "mshta.exe """ & sWizardHTA & """ /NotWizard /LeaveShareOpen /ValidateAgainstUNCPath:""" & sServerShare & """ /Definition:Credentials_ENU.xml", 1, true

			' See if the values are populated now

			If oEnvironment.Item("UserID") = "" then
				oLogging.CreateEntry "ERROR - no credentials were returned from LTICredentials.hta, so no connection is possible.", LogTypeError
				ValidateConnectionEx = Failure
				Exit Function
			End if

		End if


		' Map a drive
		
		Dim iTryIteration
		Dim bAnotherNAA
		iTryIteration = 1
		bAnotherNAA = True
		
		Do	
			If oEnvironment.Item("UserDomain") <> "" then
				sUserID = oEnvironment.Item("UserDomain") & "\" & oEnvironment.Item("UserID")
			Else
				sUserID = oEnvironment.Item("UserID")
			End if

			For i = 1 to 5
				If MapNetworkDrive(sServerShare, sUserID, oEnvironment.Item("UserPassword")) = Success then
					Exit for
				End If

				oLogging.CreateEntry "Unable to connect to " & sServerShare & ".  Sleeping for " & CStr(i * 5) & " seconds.", LogTypeInfo
				On error Resume next
				WScript.Sleep i * 5000
				on error goto 0
			Next

			If IsNetworkDriveMapped(sServerShare) = True Then Exit Do
			
			'After 5 failures, try another account
			bAnotherNAA = GetNextNAACred(iTryIteration)
			iTryIteration = iTryIteration + 1
				
			If Not bAnotherNAA Then
				oLogging.CreateEntry "ERROR - Unable to map a network drive to " & sServerShare & ".", LogTypeError
				ValidateConnectionEx = Failure
				Exit Function
			End If
			
		Loop While bAnotherNAA = True

		' Record the mapped drive

		oLogging.CreateEntry "Successfully established connection using supplied credentials.", LogTypeInfo
		If not dicNetworkConnections.Exists(sServerName) then
			dicNetworkConnections.Add sServerName, sServerShare
		End if

		iRetVal = Success


		ValidateConnectionEx = iRetVal

	End Function


	Function ValidateNetworkConnectivity

		Dim Entity, ID, oAdapter, oAdapter2, colAdapters, colAdapters2, sIPConnectionMetric, sWirelessConnectionMetric
		Dim bValidIP

		' Check for networkadapters present

		bValidIP = True
		If objWMI.ExecQuery("select * from win32_NetworkAdapter where Installed = true and adaptertypeid = 0").Count = 0 then
			oLogging.CreateEntry "No networking adapters found, The network drivers for your device are not present",LogTypeError
			ValidateNetworkConnectivity = Failure
			exit function
	
		End if


		'Check for IP address

		Set colAdapters = objWMI.ExecQuery("select * from win32_NetworkAdapterConfiguration where IPEnabled=True")

		For Each oAdapter in colAdapters
			If oAdapter.DHCPEnabled = TRUE Then
				If oAdapter.DHCPServer = "255.255.255.255" Then
					bValidIP = False
				
				Else
					bValidIP = True
					Exit For
				End if
				
			Else
				oLogging.CreateEntry "DHCP is not enabled, assuming static IP address", LogTypeInfo
				bValidIP = True
				Exit For
			End if
		Next

		If bValidIP <> True Then
			'No IP Address, do an ipconfig /renew
			oShell.Run "ipconfig /renew",0,true
			on error resume next
			wscript.sleep 5000
			on error goto 0
			Set colAdapters2 = objWMI.ExecQuery("select * from win32_NetworkAdapterconfiguration where IPEnabled = True")
			For Each oAdapter2 in colAdapters2
				If oAdapter2.DHCPEnabled = TRUE Then
					If oAdapter2.DHCPServer = "255.255.255.255" Then
						oLogging.CreateEntry "IP Address could not be obtained",LogTypeError
						ValidateNetworkConnectivity = Failure
						Exit Function
					Else
						Exit for
					End if
				End if
			Next
		End If

		'Check for wireless connectivity

		Set colAdapters = objWMI.ExecQuery("select * from win32_NetworkAdapterconfiguration where IPEnabled = True")
		For Each oAdapter in colAdapters
			If Instr(UCase(oAdapter.Caption),"WIRELESS") = 0 Then
				If oAdapter.IPConnectionMetric < sIPConnectionMetric Or sIPConnectionMetric = "" Then
					sIPConnectionMetric = oAdapter.IPConnectionMetric
				End If
			End IF
			
			If Instr(UCase(oAdapter.Caption),"WIRELESS") Then
				sWirelessConnectionMetric = oAdapter.IPConnectionMetric
			
		
			End If
		
		Next
		
		If sIPConnectionMetric = "" Then
			oLogging.CreateEntry "No physical adapters present, cannot deploy over wireless", LogTypeError
			ValidatenetworkConnectivity = Failure
			Exit Function
		End IF


		'TODO Check for VPN connectivity

		ValidateNetworkConnectivity = Success

	End Function


	Function IsNetworkDriveMapped(sShare)
		IsNetworkDriveMapped = False
		
		If IsEmpty(sShare) Or IsNull(sShare) Or TypeName(sShare) = "Nothing" Then 
			oLogging.CreateEntry "Specified network path is null or invalid.", LogTypeError
			Exit Function
		End if
	
		Dim i
		Dim arrDrives
		
		On Error Resume Next
		Set arrDrives = oNetwork.EnumNetworkDrives
		
		If Err then
			oLogging.CreateEntry "Unable to enumerate network drives (is the network initialized?): " & Err.Description & " (" & Err.Number & ")", LogTypeWarning
		Else

			' Find any previous connections (skip connections without drive letters)

			For i = 0 to arrDrives.Count - 1 Step 2
				If UCase(sShare) = UCase(arrDrives(i+1)) and arrDrives(i) <> "" then
					IsNetworkDriveMapped = True
					oLogging.CreateEntry "Found Existing UNC Path " & arrDrives(i) & " = " & sShare , LogTypeInfo

					Exit function
				End if
			Next

		End if
	End Function
	
	' For Backwards Compatiblity
	Function MapNetworkDrive (sShare, sDomID, sDomPwd )

		' Make sure networking is initialized
		If IsNetworkDriveMapped(sShare) Then
			MapNetworkDrive = Success
			Exit Function
		End If
		
		If Len(MapNetworkDriveEx (sShare, sDomID, sDomPwd, LogTypeError )) = 2 then
			MapNetworkDrive = Success
		Else
			MapNetworkDrive = Failure
		End if
	End function


	'
	' maps a drive letter to the sShare UNC path.
	'   Returns the drive letter example: "C:", otherwise returns an error string!
	'   sDomID and sDomPwd can be EMPTY.
	'
	Function MapNetworkDriveEx (sShare, sDomID, sDomPwd, iLogType )

		Dim sDrive
		Dim HasError
		Dim ErrDesc
		Dim i
		


		On Error Goto 0


		' Find the first available drive letter

		For sDrive = asc("Z") to asc("C") step -1

			On Error Resume Next
			If sDomID <> "" and sDomPwd <> "" then
				oNetwork.MapNetworkDrive  chr(sDrive)&":", sShare, False, sDomID, sDomPwd
			Else
				oNetwork.MapNetworkDrive  chr(sDrive)&":", sShare, False
			End if
			HasError = err.number
			ErrDesc = err.Description
			On Error Goto 0

			Select case HasError
			Case 0          ' No Error, SUCCESS
				MapNetworkDriveEx = chr(sDrive)&":"
				oLogging.CreateEntry "Mapped Network UNC Path " & MapNetworkDriveEx & "  = " & sShare , LogTypeInfo
				Exit function
			Case &h80070055 ' The local device name is already in use.
			Case &h800704B2 ' The local device name has a remembered connection to another network resource.
			' Case &h800704C3 ' Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed.
			' Case &h8007052E ' Logon failure: unknown user name or bad password.			
			Case Else 
				oLogging.CreateEntry "Unable to connect to share: " & ErrDesc & "( 0x" & hex(HasError) & " ) , trying to connect without username. ", LogTypeInfo
				Err.Clear
				On Error Resume Next
				oNetwork.MapNetworkDrive chr(sDrive)&":", sShare, False
				HasError = err.number
				ErrDesc = err.Description
				On Error Goto 0
				If HasError <> 0 Then

					' There was a some kind of fatal error.
					If ErrDesc <> "" then
						MapNetworkDriveEx = ErrDesc
					Else
						MapNetworkDriveEx = "Unable to map UNC Path " & sShare & " :" & "( 0x" & hex(HasError) & " ) "
					End if
					oLogging.CreateEntry MapNetworkDriveEx & "", iLogType
					Exit function
				Else
					MapNetworkDriveEx = chr(sDrive)&":"
					Exit Function
				End If		
			End select

		Next

		MapNetworkDriveEx = "Unable to map UNC Path " & sShare & " : No available local device names! "
		oLogging.CreateEntry MapNetworkDriveEx , iLogType

	End function


	Public Function VerifyPathExistsEx(strPath, bAdjustSD)
		Dim oHelper, sSDDL, oLogicalFile, oDescriptor
		If strPath = "" then
			VerifyPathExistsEx = True
			Exit Function
		End if
		If oFSO.FolderExists(strPath) then
			VerifyPathExistsEx = true
			Exit Function
		Else
			VerifyPathExists oFSO.GetParentFolderName(strPath)
			On Error Resume Next
			oFSO.CreateFolder strPath
			
			'Set Permissions if it is not a UNC path
			If Left(strPath, 2) <> "\\" and bAdjustSD Then
				strPath = Replace(strPath, "\", "\\")

				Set oHelper = objWMI.Get("Win32_SecurityDescriptorHelper")

				sSDDL = "O:BAG:SYD:PAI(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)"

				oHelper.SDDLToWin32SD sSDDL, oDescriptor
				Set oLogicalFile = objWMI.Get("Win32_LogicalFileSecuritySetting.Path='" & strPath & "'")
				TestAndLog oLogicalFile.SetSecurityDescriptor(oDescriptor), "Set security on folder " & strPath
				
			End If
			On Error Goto 0
		End if
	End function 
	
	Public Function VerifyPathExists(strPath)
		VerifyPathExists = VerifyPathExistsEx(strPath, false)
	End Function


	Function GetAllFixedDrives(bReturnOnlyBootable)

		Dim oWMIDiskPart
		Dim oDiskPart
		Dim oDisk

		oLogging.CreateEntry "ZTIUtility!GetAllFixedDrives (" & bReturnOnlyBootable & ")", LogTypeInfo

		for each oWMIDiskPart in AllDiskPart
			set oDiskPart = new ZTIDiskPartition
			set oDiskPart.DiskPart = oWMIDiskPart
			set oDisk = oDiskPart.GetDiskObject
			If oDisk.IsOSReady("0.0.0.0") then
				GetAllFixedDrives = GetAllFixedDrives & " " & oDiskPart.Drive
			End if
		next

		oLogging.CreateEntry "ZTIUtility!GetAllFixedDrives = " & GetAllFixedDrives, LogTypeInfo

		GetAllFixedDrives = split(trim(GetAllFixedDrives)," ")

	End function

	'
	' Create an XMLDOM Object
	'
	Function CreateXMLDOMObjectEx( sFileName )
	
		Dim bRetVal

		on error resume next
		
		Set CreateXMLDOMObjectEx = nothing
		Set CreateXMLDOMObjectEx = oUtility.GetMSXMLDOMDocument
		TestAndFail not (CreateXMLDOMObjectEx is nothing), 5490, "Create MSXML2.DOMDocument."
		
		CreateXMLDOMObjectEx.Async = FALSE

		If sFileName <> "" then
		
			oLogging.CreateEntry "CreateXMLDOMObjectEx(" & sFileName & ")", LogTypeVerbose
			bRetVal = CreateXMLDOMObjectEx.Load (sFileName )
			TestAndLog bRetVal, "CreateXMLDOMObjectEx...Length = " & CreateXMLDOMObjectEx.documentElement.childNodes.Length
			With CreateXMLDOMObjectEx.ParseError
				If .errorCode <> 0 then
					oLogging.CreateEntry "File: " & sFileName & " Line: " & .Line & " - " & .Reason & " - " & .SrcText, LogTypeWarning
				End if
			End with
			
		End if
		
		on error goto 0

	End function
	

	' Create an XMLDOM Object, do not return if there is a parse error.
	Function CreateXMLDOMObjectSafe( sFileName )
	
		Set CreateXMLDOMObjectSafe = CreateXMLDOMObjectEx( sFileName )
		TestAndFail CreateXMLDOMObjectSafe.ParseError.ErrorCode, 5495, "Create MSXML2.DOMDocument  .ParseErr.ErrCode."
		
	End function
	

	
	Function CreateXMLDOMObject
		Set CreateXMLDOMObject = CreateXMLDOMObjectEx( empty )
	End function


	' Load an XML file via FindFile
	Function LoadConfigFile( sConfigFile )

		Set LoadConfigFile = LoadConfigFileEx( sConfigFile, false )

	End function
	
	
	' Load an XML file via FindFile, do not return if there is any error.
	Function LoadConfigFileSafe( sConfigFile )
	
		Set LoadConfigFileSafe = LoadConfigFileEx( sConfigFile, TRUE )

	End function
	

	' Load an XML file via FindFile, do not return if there is any error.
	Function LoadConfigFileEx( sConfigFile, bMustSucceed )
	
		Dim sFoundFile
		Dim iRetVal

		iRetVal = oUtility.FindFile( sConfigFile , sFoundFile)
		If bMustSucceed then
			TestAndFail iRetVal, 5496, "LoadControlFile.FindFile: " & sConfigFile
			set LoadConfigFileEx = CreateXMLDOMObjectSafe (sFoundFile)
		Else
			set LoadConfigFileEx = CreateXMLDOMObjectEx (sFoundFile)
		End if

	End function


	Function BDDUtility

		Dim sBDDUtility
		Dim iRetval
		Dim sProc


		' Already retrieved an instance?  Return it.
		If not (oBDDUtility is Nothing) then
			Set BDDUtility = oBDDUtility
			Exit Function
		End if


		' Already registered? Call Routine.
		on error resume next
		Set oBDDUtility = CreateObject("Microsoft.BDD.Utility")
		on error goto 0

		If not (oBDDUtility is Nothing) then
			Set BDDUtility = oBDDUtility
			Exit Function
		End if


		' Find each Microsoft.BDD.Utility.dll file, and copy locally if over the network.
		for each sProc in array("x86","x64")

			If not oFSO.FileExists(oEnv("TEMP") & "\Tools\" & sProc & "\Microsoft.BDD.Utility.dll") then

				iRetVal = FindFile( sProc & "\Microsoft.BDD.Utility.dll", sBDDUtility)
				oLogging.CreateEntry "FindFile(...\Microsoft.BDD.Utility.dll)  Result : " & iRetVal, LogTypeInfo

				If left(sBDDUtility,2) = "\\" then
					oUtility.VerifyPathExistsEx oEnv("TEMP") & "\Tools\" & sProc , false
					oFileHandling.CopyFile sBDDUtility, oEnv("TEMP") & "\Tools\" & sProc & "\", True
				End if

			End if
			
			If oFSO.FileExists(oEnv("TEMP") & "\Tools\" & sProc & "\Microsoft.BDD.Utility.dll") then
				sBDDUtility = oEnv("TEMP") & "\Tools\" & sProc & "\Microsoft.BDD.Utility.dll"
			End if

			' Register the DLL
			oLogging.CreateEntry "RUN: regsvr32.exe /s """ & sBDDUtility & """", LogTypeInfo
			oShell.Run "regsvr32.exe /s """ & sBDDUtility & """", 0, true ' Always returns 0 - Success

		next



		' Create an instance

		on error resume next
		Set oBDDUtility = CreateObject("Microsoft.BDD.Utility")
		TestAndLog not oBDDUtility is nothing, "CreateObject(Microsoft.BDD.Utility)"
		on error goto 0
		
		Set BDDUtility = oBDDUtility

	End Function

	Private Sub SetIfChanged(sVar, sVal)
		If oEnvironment.Item(sVar) <> sVal then
			oEnvironment.Item(sVar) = sVal
		End if
	End Sub

	Sub SetTaskSequenceProperties(tsID)

		Dim oTaskSequences
		Dim oTaskSequence
		Dim oOperatingSystems
		Dim oOS
		Dim oImageLang
		Dim sImageLang
		Dim oLanguage
		Dim sImagePath
		Dim oWDSServer
		Dim sWDSServer
		Dim objTmp
		Dim oTS
		Dim oOSGUID
		Dim oDestinationDisk,oDestinationPartition,ODestinationLogicalDrive
		Dim sSourcePath


		' If there is task sequence ID set, get the properties

		If tsID <> "" then

			' Get the task sequence record

			tsID = Ucase(tsID)

			Set oTaskSequences = oUtility.CreateXMLDOMObjectEx(oEnvironment.Item("DeployRoot") & "\Control\TaskSequences.xml")
			Set oTaskSequence = oTaskSequences.selectSingleNode("//ts[ID='" & tsID & "']")
			If oTaskSequence is Nothing then
				oLogging.CreateEntry "ERROR: Invalid task sequence ID " & tsID & " specified", LogTypeError
				Exit Sub
			End if


			' Set the simple build properties

			oEnvironment.Item("TaskSequenceName") = oUtility.SelectSingleNodeString(oTaskSequence,"Name")
			oEnvironment.Item("TaskSequenceVersion") = oUtility.SelectSingleNodeStringEx(oTaskSequence,"Version", False)
			oEnvironment.Item("TaskSequenceTemplate") = oUtility.SelectSingleNodeStringEx(oTaskSequence,"TaskSequenceTemplate", False)


			' Blank out any previous values			

			SetIfChanged "ImageIndex", ""
			SetIfChanged "ImageSize", ""
			SetIfChanged "ImageFlags", ""
			SetIfChanged "ImageBuild", ""
			SetIfChanged "InstallFromPath", ""
			SetIfChanged "ImageMemory", ""
			SetIfChanged "ImageProcessor", ""
			SetIfChanged "OSGUID", ""
			SetIfChanged "IsOSUpgrade", ""


			' Load the TS.XML

			Set oTS = oUtility.CreateXMLDOMObjectEx(oEnvironment.Item("DeployRoot") & "\Control\" & tsID & "\TS.xml")


			' Determine the deployment type

			If (oTS.SelectSingleNode("//step[@type='BDD_InstallOS' and @disable='false']") is nothing) and (oTS.SelectSingleNode("//step[@type='BDD_UpgradeOS' and @disable='false']") is nothing) then
				oLogging.CreateEntry "Task Sequence does not contain an OS and does not contain a LTIApply.wsf step, possibly a Custom Step or a Client Replace.", LogTypeInfo
				oEnvironment.Item("OSGUID")=""
				If not (oTS.SelectSingleNode("//group[@name='State Restore']") is nothing) then
					oEnvironment.Item("DeploymentType") = "StateRestore"
				ElseIf oEnvironment.Item("TaskSequenceTemplate") <> "ClientReplace.xml" and oTS.SelectSingleNode("//step[@name='Capture User State']") is nothing then
					oEnvironment.Item("DeploymentType") = "CUSTOM"
				Else
					oEnvironment.Item("DeploymentType") = "REPLACE"
					oEnvironment.Item("ImageProcessor") = Ucase(oEnvironment.Item("Architecture"))
				End if

			Elseif oEnvironment.Item("OSVERSION")="WinPE" Then

				oEnvironment.Item("DeploymentType") = "NEWCOMPUTER"		

			Else

				oLogging.CreateEntry "Task Sequence contains a LTIApply.wsf step, and is not running within WinPE.", LogTypeInfo
				If oTS.SelectSingleNode("//globalVarList/variable[@name='IsOSUpgrade']") is nothing then
					oEnvironment.Item("DeploymentType") = "REFRESH"
				Else 
					oEnvironment.Item("DeploymentType") = "UPGRADE"
					oEnvironment.Item("IsOSUpgrade") = "1"
				End if

			End if

			If oEnvironment.Item("IsOSUpgrade") = "" then
				oEnvironment.Item("IsOSUpgrade") = "0"
			End if


			' Get the OS details

			Set oOSGUID = oTS.SelectSingleNode("//globalVarList/variable[@name='OSGUID']")
			If not (oOSGUID is Nothing) then

				' Get the OS record

				oEnvironment.Item("OSGUID") = oOSGUID.text
				Set oOperatingSystems = oUtility.CreateXMLDOMObjectEx(oEnvironment.Item("DeployRoot") & "\Control\OperatingSystems.xml")
				Set oOS = oOperatingSystems.selectSingleNode("//os[@guid='" & oOSGUID.text & "']")
				If oOS is Nothing then
					oLogging.CreateEntry "ERROR: Invalid OS GUID " & oOSGUID.text & " specified for task sequence " & tsID & " specified", LogTypeInfo
					Exit Sub
				End if


				' Set the simple OS properties

				oEnvironment.Item("ImageIndex") = oUtility.SelectSingleNodeString(oOS,"ImageIndex")
				oEnvironment.Item("ImageSize") = oUtility.SelectSingleNodeString(oOS,"Size")
				on error resume next
				oEnvironment.Item("ImageFlags") = oOS.selectSingleNode("Flags").text
				on error goto 0
				oEnvironment.Item("ImageBuild") = oUtility.SelectSingleNodeString(oOS,"Build")
				oEnvironment.Item("ImageProcessor") = oUtility.SelectSingleNodeString(oOS,"Platform")


				' Get the languages

				Set oImageLang = oOS.selectNodes("Language")
				sImageLang = ""
				If not (oImageLang is Nothing) then
					For each oLanguage in oImageLang
						sImageLang = sImageLang & oLanguage.text & vbTab
					Next
				End if
				If right(sImageLang,1) = vbTab then
					sImageLang = Left(sImageLang, Len(sImageLang)-1)  ' Remove trailing tab
				End if
				oEnvironment.ListItem("ImageLanguage") = split(sImageLang, vbTab)


				' Set the image path

				If oOS.selectSingleNode("ImageFile") is nothing then
					sImagePath = "."
				Else
					sImagePath = oOS.selectSingleNode("ImageFile").Text
					If sImagePath = "" and left(oEnvironment.Item("ImageBuild"),1) = "5" then
						sImagePath = "."
					End if
				End if

				If Left(sImagePath, 1) = "." then

					' See if this is a WDS image

					Set oWDSServer = oOS.selectSingleNode("WDSServer")
					If not (oWDSServer is Nothing) then
						sWDSServer = oWDSServer.Text
					End if


					' Make sure that's where we want to pull it from

					If sWDSServer <> "" then
						If oEnvironment.Item("WDSServer") <> "" then
							sWDSServer = oEnvironment.Item("WDSServer")
						End if
					End if


					' Set the actual image path

					If sWDSServer <> "" then
						sImagePath = "\\" & sWDSServer & "\REMINST" & Mid(sImagePath, 2)
					Else
						sImagePath = oEnvironment.Item("DeployRoot") & Mid(sImagePath, 2)
					End if

				End if

				oLogging.CreateEntry "InstallFromPath: " & sImagePath, LogTypeInfo
				oEnvironment.Item("InstallFromPath") = oFileHandling.NormalizePath(sImagePath)

				sSourcePath = oUtility.SelectSingleNodeString(oOS,"Source")
				If Left(sSourcePath, 1) = "." then
					sSourcePath = oEnvironment.Item("DeployRoot") & Mid(sSourcePath, 2)
				End if
				oLogging.CreateEntry "SourcePath: " & sSourcePath, LogTypeInfo
				oEnvironment.Item("SourcePath") = sSourcePath

			End if

		End if

	End Sub


	Function IsSupportedPlatform(sPlatform)

		Dim iRetVal
		Dim sSupportedPlatforms
		Dim oPlatformNode
		Dim oNode
		Dim oResults
		Dim oResult
		Dim bFound


		' Special case: check for Windows PE

		If oEnvironment.Item("OSVersion") = "WinPE" then

			If sPlatform = "Windows PE" then
				IsSupportedPlatform = true
			Else
				IsSupportedPlatform = false
			End if

			Exit Function
		End if


		' Load the XML file if not yet loaded


		If oSupportedPlatforms is Nothing then

			iRetVal = oUtility.FindFile("ZTISupportedPlatforms.xml", sSupportedPlatforms)
			Set oSupportedPlatforms = CreateXMLDOMObjectEx(sSupportedPlatforms)

		End if


		' Find the selected platform

		Set oPlatformNode = oSupportedPlatforms.SelectSingleNode("//SupportedPlatform[@name='" & sPlatform & "']")
		If oPlatformNode is Nothing then
			oLogging.CreateEntry "Platform " & sPlatform & " is not found.", LogTypeInfo
			IsSupportedPlatform = False
			Exit Function
		End if


		' Check each of the expressions.  If any don't return a record, return false

		For each oNode in oPlatformNode.SelectNodes("Expression")

			bFound = false
			Set oResults = objWMI.ExecQuery(oNode.Text)
			For each oResult in oResults
				bFound = true
				Exit For
			Next

			If not bFound then
				oLogging.CreateEntry "Condition " & oNode.Text & " not satisfied, platform " & sPlatform & " is not supported.", LogTypeInfo
				IsSupportedPlatform = False
				Exit Function
			End if

		Next


		' All conditions satisfied, return true

		oLogging.CreateEntry "Platform " & sPlatform & " is supported on this computer.", LogTypeInfo
		IsSupportedPlatform = True

	End Function


	Function SetTagForDrive( sDriveLetter, sTagVariable )

		Dim oDiskPart
		oLogging.CreateEntry "Set the Tag variable: " & sTagVariable, LogTypeInfo
		oEnvironment.Item(sTagVariable) = ""
		For each oDiskPart in objWMI.ExecQuery( "SELECT * FROM Win32_LogicalDisk WHERE DeviceID = '" & left(sDriveLetter,2) & "'" )
			oEnvironment.Item(sTagVariable) = "SELECT * FROM Win32_LogicalDisk WHERE Size = '" & oDiskPart.Size & "' and VolumeName = '" & oDiskPart.VolumeName & "' and VolumeSerialNumber = '" & oDiskPart.VolumeSerialNumber & "'"
			exit For
		next

	End function 

	Function GetDriveFromTag ( sTagVariable )

		Dim oDiskPart
		oLogging.CreateEntry "Search for Drive: " & sTagVariable & "  " & oEnvironment.Item(sTagVariable), LogTypeInfo
		For each oDiskPart in objWMI.ExecQuery( oEnvironment.Item(sTagVariable) )
			oLogging.CreateEntry "Found Drive: " & oDiskPart.DeviceID, logTypeInfo
			GetDriveFromTag = left(oDiskPart.DeviceID,2)
			Exit For
		next

	End function 


	Function ClearRelativeDriveLetters

		oLogging.CreateEntry "If there is a drive letter defined, make sure we clear it now so we can *force* recalcutation.", logTypeInfo
		oEnvironment.Item("OSDTargetDriveCache") = "DIRTY"

	End function

	Function ForceRelativeDriveReCalc

		oLogging.CreateEntry "We can no longer assume any information on drive letters, reset everything!.", logTypeInfo
		ClearRelativeDriveLetters
		If oEnvironment.Item("TargetPartitionIdentifier") <> "" Then
			oEnvironment.Item("TargetPartitionIdentifier") = "DIRTY"
		End if

	End function


	Function GetOSTargetDriveLetterEx( bRequired )

		Dim oDiskPart
		Dim bDiskPartFound


		' First check to see if the full OS is running
		If oEnv("SystemDrive") <> "X:" then

			' We are not running within WinPE, Drive Letter may be easy to determine.
			If ucase(oEnvironment.Item("DestinationOSRefresh")) = "OKTOUSEOTHERDISKANDPARTITION" and ucase(oEnvironment.Item("DeploymentType")) = "REFRESH" and ucase(oEnvironment.Item("PHASE")) <> "STATERESTORE" then
				oLogging.CreateEntry "It is possible that the Existing SystemRoot is not the Destiation Drive", logTypeInfo
			Else

				oLogging.CreateEntry "TargetOS is the current SystemDrive", logTypeInfo
				GetOSTargetDriveLetterEx = left(oEnv("SystemDrive"),2)
				exit function     ' Easy case, exit now!
			End if

		End if


		' We may wish to use some advanced Disk Partitioning Routines from ZTIDIskPart.vbs. Check availabiltiy.
		On error resume next
		bDiskPartFound = false
		bDiskPartFound = not isempty(ZTIDiskPart_Script)
		on error goto 0


		If oEnvironment.Item("OriginalPartitionIdentifier") <> "" and ucase(oEnvironment.Item("DestinationOSRefresh")) <> "OKTOUSEOTHERDISKANDPARTITION" Then

			oLogging.CreateEntry "OriginalPartitionIdentifier is set, find disk: " & oEnvironment.Item("OriginalPartitionIdentifier"), logTypeInfo
			GetOSTargetDriveLetterEx = GetDriveFromTag ( "OriginalPartitionIdentifier" )


		ElseIf oEnvironment.Item("OSDTargetDriveCache") <> "" and oEnvironment.Item("OSDTargetDriveCache") <> "DIRTY" and Ucase(oUtility.ScriptName) <> "ZTIDISKPART" Then

			oLogging.CreateEntry "OSDTargetDriveCache was determined earlier : " & oEnvironment.Item("OSDTargetDriveCache"), logTypeInfo
			GetOSTargetDriveLetterEx = left(oEnvironment.Item("OSDTargetDriveCache") ,2)


		ElseIf oEnvironment.Item("TargetPartitionIdentifier") <> "" and oEnvironment.Item("TargetPartitionIdentifier") <> "DIRTY" Then

			oLogging.CreateEntry "TargetPartitionIdentifier is set, find disk: " & oEnvironment.Item("TargetPartitionIdentifier"), logTypeInfo
			GetOSTargetDriveLetterEx = GetDriveFromTag ( "TargetPartitionIdentifier" )


		ElseIf oEnvironment.Item("DeploymentMethod") <> "SCCM" then

			'
			'  Litetouch Scenarios:
			'

			If ucase(oEnvironment.Item("DestinationOSInstallType")) = "BYDRIVELETTER" and oEnvironment.Item("DestinationOSDriveLetter") <> "" then

				oLogging.CreateEntry "DestinationOSInstallType = BYDRIVELETTER (Generally not recomended scenario, drive letters *can* change between reboots).", LogTypeInfo
				GetOSTargetDriveLetterEx = left(oEnvironment.Item("DestinationOSDriveLetter") ,1) & ":"


			ElseIf ucase(oEnvironment.Item("DestinationOSInstallType")) = "BYVARIABLE" and oEnvironment.IndirectItem("DestinationOSVariable") <> "" then

				oLogging.CreateEntry "Found: DestinationOSVariable: " & oEnvironment.IndirectItem("DestinationOSVariable"), LogTypeInfo
				GetOSTargetDriveLetterEx = left(oEnvironment.IndirectItem("DestinationOSVariable") ,2)


			Elseif bDiskPartFound then

				' Uses functions within ztidiskutility.vbs

				If ucase(oEnvironment.Item("DestinationOSInstallType")) = "NEXTPARTITION" then

					oLogging.CreateEntry "Found: DestinationOSInstallType = NEXTPARTITION ", LogTypeInfo
					GetOSTargetDriveLetterEx = left(GetFirstPossibleSystemDrive,2) 

				ElseIf (ucase(oEnvironment.Item("DestinationOSInstallType")) = "BYDISKPARTITION" or oEnvironment.Item("DestinationOSInstallType") = "" ) and isnumeric(oEnvironment.Item("DestinationDisk")) and isnumeric(oEnvironment.Item("DestinationPartition")) then

					oLogging.CreateEntry "DestinationOSInstallType = BYDISKPARTITION", LogTypeInfo
					oLogging.CreateEntry "Found: DestinationDisk: " & oEnvironment.Item("DestinationDisk"), LogTypeInfo
					oLogging.CreateEntry "Found: DestinationPartition: " & oEnvironment.Item("DestinationPartition"), LogTypeInfo

					set oDiskPart = new ZTIDiskPartition
					oDiskPart.SetDiskPart oEnvironment.Item("DestinationDisk"), oEnvironment.Item("DestinationPartition")
					If isEmpty( oDiskPart.oWMIDiskPart ) then
						oLogging.CreateEntry "No partition Found!", logTypeInfo
					ElseIf IsEmpty(oDiskPart.Drive) then
						oLogging.CreateEntry "No drive mapped to Disk Partition: " & oDIskPart.oWMIDiskPart.Path_, logTypeInfo
					Else
						oLogging.CreateEntry "Disk Size : " & FormatLargeSize( oDiskPart.oWMIDiskPart.Size ), logTypeInfo
						oLogging.CreateEntry "Min Size : " &  FormatLargeSize(GetMinimumDiskPartitionSizeMB * 1000 * 1000) , logTypeInfo

						If (oDiskPart.oWMIDiskPart.Size /1000 /1000) < (GetMinimumDiskPartitionSizeMB) and 1 = cint(oEnvironment.Item("DestinationPartition")) then
							oLogging.CreateEntry "Partition is set too small, assume BDEPartition and recover.", logTypeInfo

							set oDiskPart = new ZTIDiskPartition
							oDiskPart.SetDiskPart oEnvironment.Item("DestinationDisk"), 2
							If isEmpty( oDiskPart.oWMIDiskPart ) then
								oLogging.CreateEntry "No partition Found!", logTypeInfo
							ElseIf IsEmpty(oDiskPart.Drive) then
								oLogging.CreateEntry "No drive mapped to Disk Partition: " & oDIskPart.oWMIDiskPart.Path_, logTypeInfo
							Else
								oLogging.CreateEntry "Disk Size : " & FormatLargeSize( oDiskPart.oWMIDiskPart.Size ), logTypeInfo
								oLogging.CreateEntry "Min Size : " &  FormatLargeSize(GetMinimumDiskPartitionSizeMB * 1000 * 1000) , logTypeInfo

								If (oDiskPart.oWMIDiskPart.Size /1000 /1000) > (GetMinimumDiskPartitionSizeMB) then
									GetOSTargetDriveLetterEx = left(oDiskPart.Drive,2)
									oEnvironment.Item("DestinationPartition") = "2"
								End if
							End if

						Else
							GetOSTargetDriveLetterEx = left(oDiskPart.Drive,2)
						End if
					End if

				End if

				If isempty(GetOSTargetDriveLetterEx) then

					oLogging.CreateEntry "Unable to yield a target Partition (Special Recovery option for Litetouch).", logTypeInfo
					If AllDiskDrives.Count = 1 then
						oLogging.CreateEntry "There is only one disk, so assume that this is the Destination disk.", logTypeInfo
						GetOSTargetDriveLetterEx = left(GetFirstPossibleSystemDrive,2)
					End if

				End if

			End if


		Else
		
			'
			'  SCCM Scenarios:
			'

			If oEnvironment.Item("_SMSTSMediaType") = "OEMMedia" and oEnvironment.Item("OSDUseAlreadyDeployedImage") = "TRUE" then

				oLogging.CreateEntry "Prestaged media deployment, use data path drive.", LogTypeInfo
				GetOSTargetDriveLetterEx = Left(oEnvironment.Item("_SMSTSMDataPath"), 2)

			ElseIf  oEnvironment.Item("OSDTargetDrive") <> "" Then

				oLogging.CreateEntry "OSDTargetDrive is set, find disk: " &  oEnvironment.Item("OSDTargetDrive"), logTypeInfo
				GetOSTargetDriveLetterEx = Left(oEnvironment.Item("OSDTargetDrive"), 2)


			ElseIf oEnvironment.Item("OSDisk") <> "" Then

				oLogging.CreateEntry "OSDisk is set, find disk: " &  oEnvironment.Item("OSDisk"), logTypeInfo
				GetOSTargetDriveLetterEx = Left(oEnvironment.Item("OSDisk") ,2)


			ElseIf oEnvironment.Item("OSDTemporaryDrive") <> "" Then

				oLogging.CreateEntry "OSDTemporaryDiskDrive is set. Most likely a early setup scenario.", logTypeInfo
				GetOSTargetDriveLetterEx = Left(oEnvironment.Item("OSDTemporaryDrive"), 2)

				' Hard Exit
				Exit function

			End if


		End if


		If IsEmpty(GetOSTargetDriveLetterEx) then

			oLogging.CreateEntry "DestinationDisk and Partition did not yield a target Partition.", logTypeInfo
			If bRequired then
				oLogging.ReportFailure "Unable to determine Destination Disk, Partition, and/or Drive. See BDD.LOG for more information.", 5456
			End if 

		Else

			' Cache the Deployment drive if found
			oEnvironment.Item("OSDTargetDriveCache") = GetOSTargetDriveLetterEx
			oEnvironment.Item("OSDisk") = GetOSTargetDriveLetterEx

			' Cache the Partition if not set already.
			If oEnvironment.Item("TargetPartitionIdentifier") = "" then
				SetTagForDrive left(GetOSTargetDriveLetterEx,2), "TargetPartitionIdentifier"
			End if

			oLogging.CreateEntry "Found GetOSTargetDriveLetterEx: " & GetOSTargetDriveLetterEx, logTypeVerbose

		End if


	End function 


	Function DeterminePartition
		oLogging.CreateEntry "DeterminePartition is deprecated.", logTypeDeprecated
		If true then
			DeterminePartition = false
		Else
			' Legacy solution
			ReCalculateDestinationDiskAndPartition (true)
			DeterminePartition = true
		End if
	End function

	Function ReCalculateDestinationDiskAndPartition ( bRequired ) 

		Dim sDriveLetter
		Dim oDiskPart
		Dim bDiskPartFound

		' We may wish to use some advanced Disk Partitioning Routines from ZTIDIskPart.vbs. Check availabiltiy.
		On error resume next
		bDiskPartFound = false
		bDiskPartFound = not isempty(ZTIDiskPart_Script)
		on error goto 0

		sDriveLetter = GetOSTargetDriveLetterEx(bRequired)

		If bRequired then
			TestAndFail bDiskPartFound , 5452, "Verify ZTIDiskPArt is loaded"
			set oDiskPart = new ZTIDiskPartition
			oDiskPart.Drive = sDriveLetter
			TestAndFail not isEmpty(oDiskPart.Drive),5451, "Verify oDiskPart.Drive is found!"
			oEnvironment.Item("DestinationDisk") = oDiskPart.Disk
			oEnvironment.Item("DestinationPartition") = oDiskPart.Partition
		ElseIf bDiskPartFound then
			set oDiskPart = new ZTIDiskPartition
			oDiskPart.Drive = sDriveLetter
			If not isEmpty(oDiskPart.Drive) then
				oEnvironment.Item("DestinationDisk") = oDiskPart.Disk
				oEnvironment.Item("DestinationPartition") = oDiskPart.Partition
			Else
				oLogging.CreateEntry "Drive not found: " & sDriveLetter, LogTypeINfo
			End if
		Else
			oLogging.CreateEntry "ZTIDiskPart_Script not found ", LogTypeINfo
		End if

	End function

	Function GetOSTargetDriveLetter

		GetOSTargetDriveLetter = GetOSTargetDriveLetterEx(true)

	End function


	Function SelectSingleNodeString( oXMLDomNode, sXPath )
		SelectSingleNodeString = SelectSingleNodeStringEx(oXMLDomNode, sXPath, True )
	End function 

	Function SelectSingleNodeStringEx( oXMLDomNode, sXPath, bError )

		If bError then
			'TestAndLog not oXMLDomNode is nothing, "verify oXMLDomNode is object."
		End if
		If not oXMLDomNode.SelectSingleNode(sXPath) is nothing then
			SelectSingleNodeStringEx = oXMLDomNode.SelectSingleNode(sXPath).Text
		Else
			If bError then
				oLogging.CreateEntry "SelectSingleNodeString(" & sXPath & ") Missing Node.", LogTypeWarning
			End if
			SelectSingleNodeStringEx = ""
		End if

	End function

	Function SelectNodesSafe ( oXMLDomNode, sXPath )
		set SelectNodesSafe = SelectNodesSafeEx ( oXMLDomNode, sXPath, True )
	End function

	Function SelectNodesSafeEx ( oXMLDomNode, sXPath, bError )

		If bError then
			'TestAndLog not oXMLDomNode is nothing, "verify oXMLDomNode is object."
		End if
		set SelectNodesSafeEx = oXMLDomNode.SelectNodes(sXPath)
		If SelectNodesSafeEx is nothing then
			If bError then
				oLogging.CreateEntry "SelectNodesSafeEx(" & sXPath & ") Missing Node.", LogTypeWarning
			End if
			set SelectNodesSafeEx = CreateObject("Scripting.Dictionary")  ' Empty object
		End if

	End function

	Function FindSysprepAnswerFile
		
		Dim sSysprepInf
		Dim sBuildPath
		Dim iRetVal
		Dim sBootDrive
		
		iRetVal = SUCCESS
		If oEnvironment.Item("TaskSequenceID") = "" Then
			oLogging.CreateEntry "The TaskSequenceID is blank, possibly a misconfigured customsettings.ini or task sequence",LogTypeWarning
		End If

		sBuildPath = oEnvironment.Item("DeployRoot") & "\Control\" & oEnvironment.Item("TaskSequenceID")
		If not oFSO.FolderExists(sBuildPath) then
			sBuildPath = oEnvironment.Item("DeployRoot")

		End if

		sBootDrive = oUtility.GetOSTargetDriveLetterEx(false) 
		if sBootDrive = "" then
			oLogging.CreateEntry "Destination Logical Drive was not specificed, defaulting to #:\", LogTypeInfo
			sBootDrive = "#:"  ' Unknown Boot Drive
		End if


		oLogging.CreateEntry "Looking for Sysprep.inf in " & sBootDrive & "\sysprep\Sysprep.inf", LogTypeInfo
		
		If oFSO.FileExists(oEnvironment.Item("OSDAnswerFilePathSysprep")) Then
				
			FindSysprepAnswerFile = iRetVal
			Exit Function
		
		ElseIf oFSO.FileExists(sBootDrive & "\sysprep\Sysprep.inf") then

			sSysprepInf = sBootDrive & "\sysprep\Sysprep.inf"
			oLogging.CreateEntry "Found Sysprep.inf at " & sSysprepInf & ".", LogTypeInfo

		ElseIf oFSO.FileExists("x:\sysprep\Sysprep.inf") then

			sSysprepInf = "x:\sysprep\Sysprep.inf"
			oLogging.CreateEntry "Found Sysprep.inf at " & sSysprepInf & ".", LogTypeInfo

		ElseIf oFSO.FileExists(sBuildPath & "\Sysprep.inf") then

			' Copy it locally

			sSysprepInf = sBootDrive & "\sysprep\Sysprep.inf"
			oLogging.CreateEntry "Found Sysprep.inf at " & sBuildPath & "\Sysprep.inf, will copy to " & sSysprepInf, LogTypeInfo

			If not oFSO.FolderExists(sBootDrive & "\Sysprep") then
				oFSO.CreateFolder sBootDrive & "\Sysprep"
			End if

			oFSO.CopyFile sBuildPath & "\Sysprep.inf", sSysprepInf
			oFSO.GetFile(sSysprepInf).Attributes = 0
			oLogging.CreateEntry "Copied " & sBuildPath & "\Sysprep.inf to " & sBootDrive & "\sysprep", LogTypeInfo
		ElseIf oEnvironment.Item("OSDTargetSystemDrive")<> "" and oFSO.FileExists(oEnvironment.Item("OSDTargetSystemDrive") & "\sysprep\sysprep.inf") then
			
			sSysprepInf = oEnvironment.Item("OSDTargetSystemDrive") & "\sysprep\sysprep.inf"
			oLogging.CreateEntry "Found Sysprep.inf at " & sSysprepInf & ".", LogTypeInfo

		Else
			oLogging.CreateEntry "The sysprep.inf file was not found.", LogTypeInfo
		End if
		
		oEnvironment.Item("OSDAnswerFilePathSysprep") = sSysprepInf
		
		FindSysprepAnswerFile = iRetVal

	
	End Function
	
	Function FindUnattendAnswerFile
		
		Dim sUnattendXML, sUnattendTxt
		Dim sBuildPath
		Dim iRetVal
		
		iRetVal = SUCCESS
		If oEnvironment.Item("TaskSequenceID") = "" Then
			oLogging.CreateEntry "The TaskSequenceID is blank, possibly a misconfigured customsettings.ini or task sequence",LogTypeWarning
		End If

		sBuildPath = oEnvironment.Item("DeployRoot") & "\Control\" & oEnvironment.Item("TaskSequenceID")
		If not oFSO.FolderExists(sBuildPath) then
			sBuildPath = oEnvironment.Item("DeployRoot")

		End if

		If oFSO.FileExists(sBuildPath & "\Unattend.txt") then

			' Copy it locally

			sUnattendTxt = oUtility.LocalRootPath & "\unattend.txt"
			oLogging.CreateEntry "Found Unattend.txt at " & sBuildPath & "\Unattend.txt, will copy to " & sUnattendTxt, LogTypeInfo

			oFSO.CopyFile sBuildPath & "\Unattend.txt", sUnattendTxt, true
			oFSO.GetFile(sUnattendTxt).Attributes = 0
			oLogging.CreateEntry "Copied " & sBuildPath & "\Unattend.txt to " & sUnattendTxt, LogTypeInfo

		ElseIf oFso.FileExists(oEnvironment.Item("OSDAnswerFilePath")) and Instr(1,oEnvironment.Item("OSDAnswerFilePath"),".txt",vbTextCompare) > 0 then
			sUnattendTxt = oEnvironment.Item("OSDAnswerFilePath")
			oLogging.CreateEntry "Found unattend.txt at " & sUnattendTxt,LogTypeInfo
		Else
			oLogging.CreateEntry "The unattend.txt file was not found.", LogTypeInfo
		End if


		' First see if there is already a local unattend.xml.  If not, copy one.

		
		If oFSO.FileExists(Left(oUtility.LocalRootPath, 2) & "\Windows\Panther\unattend\unattend.xml") then

			
			sUnattendXml = Left(oUtility.LocalRootPath, 2) & "\Windows\Panther\unattend\unattend.xml"
			oLogging.CreateEntry "Found existing unattend.xml at " & sUnattendXml, LogTypeInfo

		ElseIf oFSO.FileExists(oUtility.LocalRootPath & "\unattend.xml") then

			
			sUnattendXml = oUtility.LocalRootPath & "\unattend.xml"
			oLogging.CreateEntry "Found existing unattend.xml at " & sUnattendXml, LogTypeInfo

		ElseIf oFSO.FileExists(sBuildPath & "\Unattend.xml") then
			
			sUnattendXml = oUtility.LocalRootPath & "\Unattend.xml"

			oLogging.CreateEntry "Found unattend.xml at " & sBuildPath & "\Unattend.xml, will copy to " & sUnattendXml, LogTypeInfo
			oFSO.CopyFile sBuildPath & "\Unattend.xml", sUnattendXml, true
			oLogging.CreateEntry "Copied " & sBuildPath & "\Unattend.xml to " & sUnattendXml, LogTypeInfo
			oFSO.GetFile(sUnattendXml).Attributes = 0

		ElseIf oFso.FileExists(oEnvironment.Item("OSDAnswerFilePath")) and Instr(1,oEnvironment.Item("OSDAnswerFilePath"),".xml",vbTextCompare) >0 then

			sUnattendXML= oEnvironment.Item("OSDAnswerFilePath")
			oLogging.CreateEntry "Found existing unattend.xml at " & sUnattendXml, LogTypeInfo

		Else

			sUnattendXml = ""
			oLogging.CreateEntry "File " & sBuildPath & "\Unattend.xml does not exist, unable to copy", LogTypeInfo

		End if
		
		If sUnattendTxt <> "" AND sUnattendXML <> "" Then
			oLogging.CreateEntry "Found an unattend.xml and an unattend.txt file, Invalid configuration", LogTypeError
			iRetVal = FAILURE
			FindUnattendAnswerFile = iRetVal
			Exit Function
		ElseIf sUnattendTxt <> "" AND sUnattendXML = "" Then
			oEnvironment.Item("OSDAnswerFilePath") = sUnattendTxt
		ElseIF sUnattendXML <> "" AND sUnattendTxt = "" Then
			oEnvironment.Item("OSDAnswerFilePath") = sUnattendXML
		Else
			oLogging.CreateEntry "No answer file could be found",LogTypeWarning
		End If
		
		FindUnattendAnswerFile = iRetVal
		
	
	End Function
	
	Function IsHighEndSKUEx( sSKU )
	
		' Windows Ultimate/Enterprise and Server SKU's allow for some
		' higher-end features, like Bitlocker and Multiple Language Packs.
		
		select case (ucase(trim(sSKU)))
			case "ULTIMATE", "ULTIMATEE", "ULTIMATEN"
				IsHighEndSKUEx = TRUE
			case "ENTERPRISE", "ENTERPRISEN", "ENTERPRISES", "ENTERPRISESN"
				IsHighEndSKUEx = TRUE
			case "PROFESSIONAL", "PROFESSIONALN"
				IsHighEndSKUEx = TRUE
			case "EDUCATION", "EDUCATIONN"
				IsHighEndSKUEx = TRUE
			case "HYPERV"
				IsHighEndSKUEx = TRUE
			case "PRERELEASE"
				IsHighEndSKUEx = TRUE
			case else
				If Instr(1, ucase(trim(sSKU)), "SERVER", vbTextCompare) > 0 then
					IsHighEndSKUEx = TRUE
				Else
					IsHighEndSKUEx = FALSE
				End if
		End Select
		
	End function
	
	Function IsHighEndSKU
		TestAndLog oEnvironment.Item("OSSKU") <> "", "Verify %OSSKU% is defined."
		IsHighEndSKU = IsHighEndSKUEx( oEnvironment.Item("OSSKU") )
	End function 


	Function InternetFileDownload(sInternetURL, sDest)
		Dim oInternetBuffer
		Dim oADODB

		' Assume failure

		InternetFileDownload = false


		' Create the web request using ADO

		Set oADODB = CreateObject("ADODB.Stream")
		Set oInternetBuffer = CreateObject("Msxml2.XmlHttp")
		oInternetBuffer.open "GET", sInternetURL, false
		On Error Resume Next
		oInternetBuffer.send ""
		On Error Goto 0


		' Check that the request is completed (ReadyState=4).  If it isn't, log it as a warning (will likely fail below
		' because the status won't be 200).

		If oInternetBuffer.ReadyState = 4 then
			oLogging.CreateEntry "Status: " & oInternetBuffer.Status & " " & sInternetURL, LogTypeInfo
		Else
			oLogging.CreateEntry "Ready State : " & oInternetBuffer.ReadyState & " " & sInternetURL, LogTypeWarning
		End if


		' If the status indicates a successful download, save the file to the specified path

		If oInternetBuffer.Status = 200 then
			If oADODB.State <> 0 then ADODB.Close
			oADODB.Type = 1 '(1=binary,2=Text)
			oADODB.Mode = 3 '(1=Read,2=Write,3=RW)
			oADODB.Open
			oADODB.Write oInternetBuffer.ResponseBody
			oADODB.SaveToFile sDest, 2
			oADODB.Close

			InternetFileDownload = True
		End if

	End function

End Class


'
'  Common String Processing Routines
'
Class Strings


	Function isNullOrEmpty( sString )

		isNullOrEmpty = false
		If isEmpty(sString) or isNull(sString) then
			isNullOrEmpty = True
		ElseIf VarType(sString) = 8 then
			isNullOrEmpty = (sString = "")
		End if

	End function

	' Create a delimited list of items.
	Sub AddToList(byref List, Item, Delimiter)  
		if isempty(list) then 
			List = cstr(item)
		else 
			list = list & delimiter & cstr(Item)
		end if 
	end Sub 

	' Display a Hex value with width
	Function HexWidth ( Value, Width  ) 
		HexWidth = right( "00000000" & hex ( value  ), Width)
	end Function 

	' Display a Hex value with width
	Function HexWidthByte ( Value, Width  ) 
		HexWidthByte = right( "00000000" & hex ( ascb( value  ) ), Width ) 
	end Function 

	Function IsWhiteSpace (MyChar)
		' Whitespace defined as vtTab[9], vbLF[10], vbVerticalTab[11], vbFormFeed[12], vbCr[13]
		IsWhiteSpace = MyChar = " " or ( MyChar >= chr(9) and MyChar <= chr(13) ) or MyChar = chr(160)
		end Function 

	Function TrimAllWS( MyString )
		TrimAllWS = MyString
		While len(TrimAllWS) > 0 and IsWhiteSpace(left(TrimAllWS,1))
			TrimAllWS = Mid(TrimAllWS,2)
		wend
		While len(TrimAllWS) > 0 and IsWhiteSpace(right(TrimAllWS,1)) 
			TrimAllWS = Mid(TrimAllWS,1,len(TrimAllWS)-1)
		wend   
	end Function

	Function RightAlign( MyString, Width ) 
		RightAlign = Right( Space(Width) & MyString, Width )
	end Function

	Function LeftAlign( MyString, Width ) 
		LeftAlign = Left( MyString & Space(Width), Width )
	end Function

	'
	' Force a value to a string format.
	'   Non-printable types will return empty.
	'
	Function ForceAsString ( InputVar )
		dim InputType, Item
		
		InputType = VarType(InputVar)

		if isObject(InputVar) or isNull(InputVar) or isEmpty(InputVar) then
			ForceAsString = "" 
		
		elseif InputType = vbError or InputType = vbVariant or InputType = vbDataObject then
			ForceAsString = "" 
			
		elseif InputType = ( vbArray or vbByte ) then
			for item = 1 to LenB( InputVar )
				AddToList ForceAsString, ForceAsString( HexWidthByte(midb(InputVar,Item,1),2) ), ""
			next 

		elseif isArray(InputVar) then
			for each item in InputVar        
				AddToList ForceAsString, ForceAsString(Item), " "   ' recurse
			next    
			
		elseif InputType = vbByte then
			ForceAsString = HexWidthByte(InputVar,2)
			
		else
			ForceAsString = cstr(InputVar)
			
		end if
	end Function 

	'
	' Given a variant, will ensure the Function returns an array
	'
	' Parameters:
	'   InputVar - Variable to convert to an array
	'   sDelimiter - OPTIONAL delimiter to force spliting in the array
	'       If sDelimiter is empty or blank, Function will return an array of 1 element.
	' Notes

	Function ForceAsArray( InputVar, sDelimiter )
		dim i

		if isArray(InputVar) then        
			ForceAsArray = InputVar 
			
		elseif VarType(InputVar) = vbObject then
			redim newarray(InputVar.Count-1)
			for i = 0 to InputVar.Count-1
				newarray(i) = InputVar(i)         
			next
			ForceAsArray = NewArray
			
		elseif VarType(InputVar) = vbString and VarType(sDelimiter) = vbString then
			ForceAsArray = split(InputVar,sDelimiter, -1, vbTextCompare )
			
		else
			ForceAsArray = array(InputVar)
			
		end if

	end Function
	
	
	Function GenerateRandomGUID 
		GenerateRandomGUID = left( CreateObject("Scriptlet.TypeLib").GUID, 38 ) 
	end Function
	
	''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	'
	'  Base64 Functions
	'

	Function BASE64_TABLE
		BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
	End function 

	Function SafeAsc ( sPlainText, pos )
		if VarType(sPlainText) = (vbArray or vbByte) then
			SafeAsc =  cint(midb(sPlainText, pos + 1 , 1))
		elseif (pos + 1) <= len(sPlainText) then
			SafeAsc = asc(mid(sPlainText, pos + 1, 1))
		else
			SafeAsc = 0
		end if
	End Function

	Function SafeEnc( n, x )
		SafeEnc = mid(BASE64_TABLE, ((n\(2^x)) and 63) + 1,1)
	End Function

	Function base64Encode( sPlainText )
		Dim i, n

		if 0 < len(sPlainText) then
			for i = 0 to len(sPlainText) - 1 step 3
				' Add a new line ...
				if i > 0  and i mod 57  = 0 then
					base64Encode = base64Encode & vbNewLine
				end if
				' three 8-bit characters become one 24-bit number
				n = (SafeAsc(sPlainText,i)*&h10000 + SafeAsc(sPlainText,i+1)*&h100 + SafeAsc(sPlainText,i+2))

				' the 24-bit number becomes four 6-bit numbers
				base64Encode = base64Encode & SafeEnc( n, 18 ) & SafeEnc( n, 12 ) & SafeEnc( n, 6 ) & SafeEnc( n, 0 )
			next
		end if

		' Pad Text at End of String
		n = (3-(len(sPlainText)mod 3)) mod 3
		base64Encode = left ( base64Encode, len(base64Encode) - n ) + string( n, "=" )

	End Function

	Function SafeDecode( s, i, x )
		SafeDecode = ( InStr(1, BASE64_TABLE, mid(s,i,1), vbBinaryCompare) - 1) * (2 ^ x)
	End Function

	Function base64Decode( sEncodedText )

		Dim sEncText
		Dim regex
		Dim p, i, n

		' Remove all non base64 text
		set regex = new RegExp


		regex.pattern = "[^=" & BASE64_TABLE & "]"
		regex.global = true

		sEncText = regex.Replace(sEncodedText,"")
		sEncText = replace( sEncText, vbLF, "")
		sEncText = replace( sEncText, vbCR, "")

		' Verify String is in Base64 format (multiple of 4 chars)
		if len(sEncText) mod 4 <> 0 then
			oLogging.CreateEntry "Variable is not a valid string (not Base64 Format)", LogTypeInfo
			base64Decode = ""			
			exit function
		end if

		if right(sEncText,2) = "==" then
			p = 2
		elseif right(sEncText,1) = "=" then
			p = 1
		end if
		sEncText = left(sEncText,len(sEncText)-p) & string(p,"A")

		for i = 1 to len(sEncText) step 4
			' Convert four 6-bit numbers into one 24 bit value
			n = SafeDecode(sEncText,i+3,0) + SafeDecode(sEncText,i+2,6) + SafeDecode(sEncText,i+1,12) + SafeDecode(sEncText,i+0,18)

			' Convert the 24-bit value back into three 8-bit values.
			base64Decode = base64Decode & chr( (n \ (2^16)) and 255 ) & chr( (n \ (2^8)) and 255 ) & chr( n and 255 )

		next

		' Trim off any excess space.
		base64Decode = left(base64Decode,len(base64Decode)-p)
	End Function



End Class


Class FileHandling


	Function RemoveFolder(sPath)
		RemoveFolder = RemoveFolderEx (sPath, TRUE)
	End Function 
	
	Function RemoveFolderEx(sPath, bLogging )

		Dim oFile, oFolder

		If bLogging then
			oLogging.CreateEntry "Remove Folder: " & sPath, LogTypeInfo
		End if 

		' Make sure the folder exists

		If not oFSO.FolderExists(sPath) then
			Exit Function
		End if


		' First try to remove any files
		on error resume next
		For each oFile in oFSO.GetFolder(sPath).Files
			DeleteFileEx oFile.Path, bLogging
		Next
		On Error Goto 0

		' Then take care of subfolders
		on error resume next
		For each oFolder in oFSO.GetFolder(sPath).Subfolders
			RemoveFolderEx oFolder.Path, bLogging
		Next
		On Error Goto 0

		' Now try to remove the folder
		On Error Resume Next
		oFSO.DeleteFolder sPath, true
		If bLogging then
			TestAndLog Err = 0, "Delete Folder: " & sPath
		End if 
		RemoveFolderEx = Err
		Err.Clear
		On Error Goto 0

	End Function
	
	Function DeleteFile(sFile)
		DeleteFile = DeleteFileEx ( sFile, TRUE )
	End function 
	
	Function DeleteFileEx(sFile, bLogging)
	
		If bLogging then
			oLogging.CreateEntry "Delete File: " & sFile, LogTypeInfo
		End if 
		On Error Resume Next
		oFSO.DeleteFile sFile, TRUE
		If bLogging then
			TestAndLog Err = 0, "Delete File: " & sFile
		End if 
		DeleteFileEx = Err
		Err.Clear
		On Error Goto 0
		
	End function
	
	Function MoveFile(sFile,sDest)
		MoveFile = MoveFileEx(sFile,sDest,True)
	End function 
	
	Function MoveFileEx(sFile,sDest,bLogging)
	
		If bLogging then
			oLogging.CreateEntry "Move File: " & sFile & " to " & sDest	, LogTypeInfo
		End if 
		On Error Resume Next
		oFSO.MoveFile sFile, sDest
		If bLogging then
			TestAndLog Err = 0, "Move File: " & sFile & " to " & sDest		
		End if 
		MoveFileEx = Err
		Err.Clear
		On Error Goto 0
		
	End function
	
	Function CopyFile(sFile,sDest, bOverwrite)
		CopyFile = CopyFileEx(sFile,sDest, bOverwrite,True)
	End function 

	Function CopyFileEx(sFile,sDest, bOverwrite,bLogging)
	
		If bLogging then
			oLogging.CreateEntry "Copy File: " & sFile & " to " & sDest	, LogTypeInfo
		End if 
		On Error Resume Next
		oFSO.CopyFile sFile, sDest, bOverwrite
		If bLogging then
			TestAndLog Err = 0, "Copy File: " & sFile & " to " & sDest		
		End if 
		CopyFileEx = Err
		Err.Clear
		On Error Goto 0
		
	End function
	
	Function CopyFolder (sSource, sDest, bOverwrite)
		CopyFolder = CopyFolderEx(sSource, sDest, bOverwrite, True)
	End function
	
	Function CopyFolderEx (sSource, sDest, bOverwrite,bLogging)
	
		If bLogging then
			oLogging.CreateEntry "Copy Folder: " & sSource & " to " & sDest	, LogTypeInfo
		End if 
		On Error Resume Next
		oFSO.CopyFolder  sSource, sDest, bOverwrite
		If bLogging then
			TestAndLog Err = 0, "Copy Folder: " & sSource & " to " & sDest		
		End if 
		CopyFolderEx = Err
		Err.Clear
		On Error Goto 0
		
	End function
	
	Function MoveFolder (sSource, sDest ) 
		MoveFolder = MoveFolderEx (sSource, sDest, True ) 
	End function 
	
	Function MoveFolderEx (sSource, sDest, bLogging ) 

		If bLogging then
			oLogging.CreateEntry "Move Folder " & sSource & " to " &  sDest , LogTypeInfo
		End if 
		On Error Resume Next
		oFSO.MoveFolder sSource,sDest
		If bLogging then
			TestAndLog Err = 0, "Move Folder " & sSource & " to " &  sDest 
		End if 
		MoveFolderEx = Err
		Err.Clear
		On Error Goto 0

	End function 

	Function NormalizePath( Path )
		NormalizePath = oFSO.GetAbsolutePathName (Path )
	End function

	Function GetTempFile
		GetTempFile = oFso.BuildPath( GetTempFolder, oFSO.GetTempName )
	End function

	Function GetTempFileEx(sExt)
		GetTempFileEx = oFso.BuildPath( GetTempFolder, oFSO.GetBaseName(oFSO.GetTempName) & "." & sExt )
	End function

	Function GetTempFolder
		GetTempFolder = oFSO.GetSpecialFolder(2) 'TemporaryFolder
	End function

	Function GetWindowsFolder
		GetWindowsFolder = oFSO.GetSpecialFolder(0) 'WindowsFolder
	End function

	Function CheckFileVersion ( sFileName, sMinVersion )
		' Returns True if the sFileName is the sMinVersion or greater, False if less, or not found.

		Dim sFoundFile
		Dim sMSIVer
		Dim sMSIVer2
		Dim i

		CheckFileVersion = oUtility.FindFile(sFileName, sFoundFile) = SUCCESS
		TestAndLog CheckFileVersion, "FindFile (" & sFileName & ")"

		If CheckFileVersion then
			sMSIVer = split(oFSO.GetFileVersion(sFoundFile),".")
			sMSIVer2 = split(sMinVersion,".")

			i = 0
			do while CheckFileVersion and ( i <= ubound(sMSIVer) ) and ( i <= ubound(sMSIVer2) )
				If sMSIVer(i) <> sMSIVer2(i) then
					CheckFileVersion = clng(sMSIVer(i)) >= clng(sMSIVer2(i))
					exit do
				End if
				i = i + 1 
			Loop

			oLogging.CreateEntry sFileName & " version: [ " & join(sMSIVer,".") & " | " & sMinVersion & " ].   Is version found? " & ( CheckFileVersion ), LogTypeInfo
		Else
			oLogging.CreateEntry sFileName & " Not found! Needs upgrade.  " & ( CheckFileVersion ), LogTypeInfo

		End if

	End function

End Class


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'
' Regular Expression Helper functions
'    http://msdn.microsoft.com/en-us/library/6wzad2b2(VS.85).aspx
'
' Usage: 
'  for each Match in oRegEx.GetRegExMatchesFromFile( "\[([^\]]*)\]","c:\boot.ini" ) 
'     Wscript.echo "Match: " & Match
'  next
'

dim g_oRegEx
Function oRegEx
	if isempty(g_oRegEx) then
		set g_oRegEx = new clsRegEx
	end if 
	set oRegEx = g_oRegEx
end Function 

class clsRegEx

	Private Function ReadEntireFile( sFileName )
		' TestAndLog oFSO.FileExists(sFileName), "File Test: " & sFileName
		ReadEntireFile = oFSO.OpenTextFile(sFileName, ForReading, FALSE ).ReadAll
	End function

	private Function RegExObj ( SearchPattern, IgnoreCase, IsGlobal ) 
		set RegExObj = New RegExp
		RegExObj.Global = IsGlobal
		RegExObj.Multiline = TRUE
		RegExObj.IgnoreCase = IgnoreCase
		RegExObj.Pattern = SearchPattern
	end Function

	Function GetRegExMatches ( SearchPattern, Buffer ) 
		set GetRegExMatches = RegExObj ( SearchPattern, TRUE, TRUE ).Execute(Buffer)
	end Function

	Function TestRegEx ( SearchPattern, Buffer ) 
		TEstRegEx = RegExObj ( SearchPattern, TRUE, TRUE ).Test(Buffer)
	end Function 

	Function ReplaceRegEx ( SearchPattern, ReplacementString, Buffer ) 
		ReplaceRegEx = RegExObj ( SearchPattern, TRUE, TRUE ).Replace(Buffer, ReplacementString)
	end Function 

	Function GetRegExMatchesFromFile ( SearchPattern, FileName ) 
		set GetRegExMatchesFromFile = GetRegExMatches ( SearchPattern, ReadEntireFile(FileName) ) 
	end Function

	Function TestRegExFromFile ( SearchPattern, FileName ) 
		TestRegExFromFile = TestRegEx ( SearchPattern, ReadEntireFile(FileName) ) 
	end Function

	Function ReplaceRegExFromFile ( SearchPattern, ReplacementString, FileName ) 
		ReplaceRegExFromFile = ReplaceRegEx ( SearchPattern, ReplacementString, ReadEntireFile(FileName) )
	end Function

	Function FindStrInFile( SearchStr, FileName )
		FindStrInFile = InStr(1, ReadEntireFile( sFileName ), SearchStr, vbTextCompare)
	end Function 


end class

