# Copyright (C) Microsoft Corporation. All rights reserved.

function GetRuntimePath([string]$fileName = $(throw "No file name is specified"))
{
    if([string]::IsNullorEmpty($fileName))
    {
        throw "Invalid file name"
    }

     [string]$runtimePath =  [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
     return Join-Path $runtimePath $fileName
}

function RegSnapin([string]$dllName = $(throw "No dll is specified"))
{
    $dllPath = ".\" + $dllName
    Import-Module $dllPath
}

function UnregSnapin([string]$dllName = $(throw "No dll is specified"))
{    
    $moduleName = $dllName.TrimEnd(".dll")
    Remove-Module $moduleName
}

function GetExistingNDFInstance($IncidentID)
{
    &{
        #if fails we start a new session
        $script:ExpectingException = $true
        $ndf = new-object -comObject ndfapi.NetworkDiagnostics.1 -strict
        $ndf.OpenExistingIncident($IncidentID); #throws exception if fails
        $script:ExpectingException = $false
        return $ndf
    }
    trap [Exception]
    {
        if($script:ExpectingException)
        {
            $script:ExpectingException = $false
            "Exception: " + $_.Exception.GetType().FullName + " Message: " + $_.Exception.Message  | convertto-xml | Update-DiagReport -id OpenExistingSessionFailure -name "Open Existing Session" -description "Failed while opening existing NDF session." -verbosity Debug
            return $null;
        }
        else
        {
            #rethrow exception
            throw $_.Exception;
        }
    }
}

function WaitWithProgress($ActivityNoDetails, $WaitHandle, $Ndf)
{
    $lastProgress = $null
    do {
        $progress = $Ndf.Progress
        if(($progress -ne $null) -and ($progress.Length -gt 0) -and !($progress -eq $lastProgress))
        {
           Write-DiagProgress -activity $progress
            $lastProgress = $progress
        }
        elseif(($progress.Length -eq 0) -and !($ActivityNoDetails -eq $lastProgress))
        {
            #clear the last progress string, use alternate description of operation
            Write-DiagProgress -activity $ActivityNoDetails
            $lastProgress = $ActivityNoDetails
        }

        &{
            $WaitHandle.Wait($ProgressUpdateDelay)
            break
        }
        trap [Exception]
        {
            #timed out, continue waiting
            continue
        }
    } while($true)
}

# if filtered mode is enabled and the root cause is not on the filter list then return TRUE
function GetRootCauseFilterValue($RootCause, [ref]$FilterMode)
{
    #assume filtering is enabled
    $filteringEnabled = $true;
    if($FilterMode)
    {
        $FilterMode.value = 0;
    }

    $regValue = get-itemproperty -path hklm:\SYSTEM\CurrentControlSet\Control\NetDiagFx\Config  -name FilterMode -ErrorAction SilentlyContinue -ErrorVariable regError
    if($regValue)
    {
        if($FilterMode)
        {
            $FilterMode.value = $regValue.FilterMode;
        }
        $filteringEnabled = !($regValue.FilterMode -eq $FM_DISABLED);
    }
    elseif($regError)
    {
        if(!($regError[0].CategoryInfo.Category -eq "InvalidArgument") -and !($regError[0].CategoryInfo.Category -eq "ObjectNotFound"))
        {
            " Warning: Unexpected error when reading FilterMode key: " + $regError[0].CategoryInfo.Category | convertto-xml | Update-DiagReport -id UnexpectedRegError -name "Unexpected Registry Error" -verbosity Debug
        }
    }

    if(!$filteringEnabled)
    {
        #allow all root causes
        return $FV_NOTFILTERED;
    }
    else
    {
        #get the registry value and make the filtering decision
        $rootCauseID = $RootCause.RootCauseID;
        $regValue = get-itemproperty -path hklm:\SYSTEM\CurrentControlSet\Control\NetDiagFx\Config\RC  -name $rootCauseID -ErrorAction SilentlyContinue -ErrorVariable regError
        if($regValue)
        {
            #root cause present, check the entry type
            $filterValue = $regValue.$rootCauseID;
            if($filterValue -eq 1)
            {
                #exclusion list, force enabled filter mode so that the RC is dropped
                if($FilterMode)
                {
                    $FilterMode.value = $FM_ENABLED;
                }
                return $FV_FILTERED;
            }
            else
            {
                #inclusion list, not filtered
                return $FV_NOTFILTERED;
            }
        }
        else
        {
            if(!($regError[0].CategoryInfo.Category -eq "InvalidArgument") -and  !($regError[0].CategoryInfo.Category -eq "ObjectNotFound"))
            {
                " Warning: Unexpected error when reading Root Cause key : " + $regError[0].CategoryInfo.Category | convertto-xml | Update-DiagReport -id UnexpectedRegError -name "Unexpected Registry Error" -verbosity Debug
            }
            #could not find root cause
            return $FV_MISSING;
        }
    }
}

function GetCatchAllInformation($RootCauseEnum, [ref]$CatchAlls, [ref]$CatchAllsIndex, [ref]$CatchAllsAltRC)
{
    $localCatchAlls  = @{}; #will contain the RC's with catch all repairs, indexed by HC it applies to
    $localCatchAllsIndex = @{}; #will hold the index of the catch-all in the root cause list
    $localCatchAllsAltRC = @{}; #will contain the alternate RC ID for the catch all, if available, indexed by root cause

    $rootCauseCount = $RootCauseEnum.Count;
    $RootCauseEnum.Reset()
    for($i=0; $i -lt $rootCauseCount; $i++)
    {
        $rootCause=$RootCauseEnum.Next;
        $repairEnum = $rootCause.Repairs;
        $repairCount = $repairEnum.Count;
        $repairEnum.Reset();
        for($curRep=0; $curRep -lt $repairCount; $curRep++)
        {
            $repair = $repairEnum.Next;
            if($repair.Flags -band $RF_RESERVED_CA)
            {
                $altRC = GetExtensionValue ($repair.DescriptionEx) ("CatchAllRC");

                #HC's this applies to
                $catchAllHCString =  GetExtensionValue ($repair.DescriptionEx) ("CatchAllHCs");
                if($catchAllHCString)
                {
                   #semi-colon separated
                   $catchAllHCList = $catchAllHCString.Split(";");
                   for($curHC=0; $catchAllHCList[$curHC]; $curHC++)
                   {
                        #if a RC is filtered for this HC, this catch all will be used
                        $localCatchAlls[$catchAllHCList[$curHC]]= $rootCause;
                        if($altRC)
                        {
                            #store the alternate RC
                            $localCatchAllsAltRC[$rootCause] = $altRC;
                        }
                   }
                   #store the index
                   $localCatchAllsIndex[$rootCause]= $i;
                }
                break;
            }
        }
    }

    #return back the values collected
    if($CatchAlls)
    {
        $CatchAlls.value = $localCatchAlls;
    }
    if($CatchAllsIndex)
    {
        $CatchAllsIndex.value = $localCatchAllsIndex;
    }
    if($CatchAllsAltRC)
    {
        $CatchAllsAltRC.value = $localCatchAllsAltRC;
    }
}

function GetParameters($DescriptionEx, $Params)
{
    $toReturn = @{};

    #this is a XML based root cause, get replacement parameters
    $paramEnum = $DescriptionEx.Parameters;
    $paramCount = $paramEnum.Count;
    $paramEnum.Reset()
    for($curP=0;$curP -lt $paramCount; $curP++)
    {
        $param = $paramEnum.Next;
        if($Params[$param.Name] -eq $null)
        {
            $toReturn += @{$param.Name = $param.Value}
        }
    }

    return $toReturn;
}

function GetKeywords($DescriptionEx)
{
    if($DescriptionEx -eq $nul)
    {
        return "";
    }

    $toReturn = "";

    #this is a XML based root cause, get replacement parameters
    $paramEnum = $DescriptionEx.Extensions;
    $paramCount = $paramEnum.Count;
    $paramEnum.Reset()
    for($curP=0;$curP -lt $paramCount; $curP++)
    {
        $param = $paramEnum.Next;
        if($param.Name -eq "Keyword")
        {
            if($toReturn.Length -gt 0)
            {
                $toReturn += "+";
            }
            $toReturn += """" + $param.Value + """";
        }
    }

    return $toReturn.Replace(" ", "+");
}

function GetExtensionValue($DescriptionEx, $ExtensionName)
{
    if($DescriptionEx)
    {
        $paramEnum = $DescriptionEx.Extensions;
        $paramCount = $paramEnum.Count;
        $paramEnum.Reset()
        for($curP=0;$curP -lt $paramCount; $curP++)
        {
            $param = $paramEnum.Next;
            if($param.Name -eq $ExtensionName)
            {
                return $param.Value;
            }
        }
    }

    return $null;
}

#are we an administrator or LUA?
function IsAdmin()
{
    $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [System.Security.Principal.WindowsPrincipal]($identity)
    return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}

#the security boundary safe data now contains two items of information
#(1) the incident ID GUID (without the { } brackets)
#(2) flags: whether the process is LUA (0) or Admin (1)
#the two items are separated by a semicolon
function GetSBSData($IncidentID)
{
    $flags = 0
    $admin = IsAdmin
    if($admin)
    {
        $flags = 1
    }

    return $IncidentID.Substring(1,$IncidentID.Length-2) +":"+$flags
}

#the function takes a security boundary safe data and splits it into
#the incident ID GUID (putting back the {} brackets), and the
#flags
function SplitSBSData($SBSData, [ref]$IncidentID, [ref]$Flags)
{
    $IncidentID.value = "{" + $SBSData.Substring(0,$SBSData.IndexOf(":")) + "}"
    $Flags.value = $SBSData.Substring($SBSData.IndexOf(":")+1)
}

function GetInstanceHashCode($RootCause, $CatchAllInUse)
{
    $hashCode = "";

    $repairEnum = $RootCause.Repairs;
    $repairCount = $repairEnum.Count;
    $repairEnum.Reset();
    if($repairCount -gt 0)
    {
        $repair = $repairEnum.Next
        if($CatchAllInUse)
        {
            #make sure we use the hash for the first catch-all repair
            for($i=0; ($i -lt $repairEnum.Count) -and (!($repair.Flags -band $RF_RESERVED_CA)); $i++)
            {
                $repair = $repairEnum.Next;
            }
        }
        $hashCode = $repair.Description.GetHashCode();
    }
    else
    {
        $hashCode = $RootCause.Description.GetHashCode();
    }

    return $hashCode;
}

function GetInstanceIDRC($RootCause, $CatchAlls, $CatchAllsAltRC)
{

    $rootCause = $RootCause;
    $rootCauseID = $RootCause.RootCauseID;

    #if catch all is in use, we need to use the catch all root cause information in the
    #instance ID so that all catch-all matches go to the same root cause in the session
    $catchAllInUse = $null;
    $filterValue = GetRootCauseFilterValue ($RootCause) ($null);
    if($filterValue -eq $FV_MISSING)
    {
        #rc is not in the filter list, catch all instance ID information should be used if present
        $className = $rootCause.ClassName;
        if($className)
        {
            #is there a chatch all fo the helper class?
            $catchAllRC = $CatchAlls[$className];
            if($catchAllRC)
            {
                #user the catch-all root cause
                $rootCause = $catchAllRC;
                #does the catch-all have an alternate root cause?
                $altRCID = $CatchAllsAltRC[$catchAllRC];
                if($altRCID)
                {
                    #use alternate root cause ID
                    $rootCauseID = $catchAllsAltRC[$catchAllRC];
                }
                $catchAllInUse = $true;
            }
        }
    }

    $hashCode = GetInstanceHashCode($rootCause) ($catchAllInUse);
    return $rootCauseID.Substring(1,$rootCauseID.Length-2) + ":" + $hashCode;
}

function GetInstanceID($RootCauseIndex, $RootCauses, $CatchAlls, $CatchAllsAltRC)
{
    #approach for generating unique instance ID:
    # (1) the root cause GUID (stripped of the { })
    # (2) the hash code  of the first repair's text
    #     the repair text is guaranteed to be unique between root causes, and constant across reruns
    #     If no repairs, then the root cause text hash code is used

    $hashCode = 0
    $rootCauseID = 0

    #find the first repair for this root cause
    $rootCauseCount = $RootCauses.Count
    $RootCauses.Reset()
    for($i=0; $i -lt $rootCauseCount; $i++)
    {
        $rootCause=$RootCauses.Next;

        #root cause index is the index of the root cause in the root cause list
        #find the specific root cause
        if($i -eq $RootCauseIndex)
        {
            break;
        }
    }

    if($i -eq $rootCauseCount)
    {
        throw "Could not find a root cause with index " + $RootCauseIndex;
    }

    return GetInstanceIDRC($rootCause) ($CatchAlls) ($CatchAllsAltRC);
}

function GetSkipReasonText($Reason)
{
    #this is for debug, OK to not localize
    if($Reason -eq $NDF_SKIPREASON_ADAPTER)
    {
        return "An interactive fix for another network interface has been attempted."
    }
    elseif($Reason -eq $NDF_SKIPREASON_DUPLICATE)
    {
        return "Another repair with the same ID applying to the same network interface has been attempted."
    }
}

function InContextEntry()
{
    $IT_HelperClassName = Get-DiagInput -ID "IT_HelperClassName"
    if($IT_HelperClassName -eq $null   -or  $IT_HelperClassName[0].Length -eq 0) {
      #Failed retriving HelperClassName from In-Context answer file
      return $null
    }

    #get input attributes
    $IT_HelperAttributes = Get-DiagInput -ID "IT_HelperAttributes"
    if($IT_HelperAttributes -eq $null) {
      #Failed retriving HelperAttributes from In-Context answer file
      return $null
    }

    return @{"HelperClassName" = $IT_HelperClassName; "HelperAttributes" = $IT_HelperAttributes}
}

function EscapeForRTF($Text)
{
    $textToEscape = $Text.Replace("\","\\")

    $sb = New-Object System.Text.StringBuilder $textToEscape.Length;
    for($i=0; $i -lt $textToEscape.Length; $i++)
    {
        $curChar = $textToEscape[$i];
        if($curChar -eq '\n')
        {
            $null = $sb.Append("\par");
        }
        elseif(($curChar -lt 0x20) -or ($curChar -eq '{') -or ($curChar -eq '}') -or ($curChar -eq '\\'))
        {
            $null = $sb.Append("\'");
            $null = $sb.Append(([int]$curChar).ToString("X2", [System.Globalization.CultureInfo]::InvariantCulture));
        }
        elseif($curChar -lt 0x80)
        {
            $null = $sb.Append($curChar);
        }
        else
        {
            $null = $sb.Append("\u");
            $null = $sb.Append(([int]$curChar).ToString([System.Globalization.CultureInfo]::InvariantCulture));
            $null = $sb.Append('?');
        }

    }

   return $sb.ToString();

}

function IsValidURL($URL)
{
    &{
        $uri = [System.URI]($URL);
        $scheme = $uri.scheme;
        if(($scheme -eq "http" ) -or ($scheme -eq "https") -or ($scheme -eq "ftp"))
        {
            return $uri.ToString();
        }
        else
        {
            return $null;
        }
    }
     trap [Exception]
    {
        return $null;
    }
}

function GetDefaultBrowser()
{
    [string]$assocString = $null
    $dll = "NetworkDiagnosticSnapIn.dll"

    try
    {
        RegSnapin $dll
    
        $assocString = [Microsoft.Windows.Diagnosis.Network.AssociationInfo]::GetAssociation("http","open");
        trap [Exception]
        {
            $assocString = $null;
        }
    }
    finally
    {
        UnregSnapin $dll
    }

    return $assocString;
}

function GetWebNDFIncidentData($URL, $DefaultConnectivity)
{
    #build entry point parameters
    $haXML = "<HelperAttributes><HelperAttribute><Name>URL</Name><Type>AT_STRING</Type><Value><![CDATA[" + $URL +  "]]></Value></HelperAttribute>"
    if($DefaultConnectivity)
    {
        #sqm explorer as the client rather than sdiaghost.exe
        $haXML += "<HelperAttribute><Name>NDFSQMCallerApplication</Name><Type>AT_STRING</Type><Value>Windows\Explorer.EXE</Value></HelperAttribute>"
        $defaultBrowser = GetDefaultBrowser;
        if($defaultBrowser)
        {
            $haXML += "<HelperAttribute><Name>AppID</Name><Type>AT_STRING</Type><Value>"+ $defaultBrowser + "</Value></HelperAttribute>"
        }
    }
    $haXML += "</HelperAttributes>"
    return @{"HelperClassName" = "WinInetHelperClass"; "HelperAttributes" =$haXML}
}

function GetValidURL($CandidateURL)
{
    $toReturn = $null
    $url = IsValidURL $CandidateURL
    if($url -eq $null)
    {
        if($CandidateURL.IndexOf("://") -eq -1)
        {
            $updatedURL = "http://" + $CandidateURL
            $url = IsValidURL $updatedURL
            if($url)
            {
                $toReturn = $url
            }
        }
    }
    else
    {
        $toReturn = $url
    }

    return $toReturn
}

function GetErrorRTF($Description, $Error)
{
  $escapedDesc = EscapeForRTF $Description;
  $escapedError = EscapeForRTF $Error;
  $rtf = LoadResourceString($ERROR_MSG_RTF_RESOURCE);
  return $rtf.Replace("%DESC%", $escapedDesc).Replace("%ERROR%", $escapedError);
}

function WebEntry()
{
    $IT_WebChoice = Get-DiagInput -ID "IT_WebChoice"
    if($IT_WebChoice -eq $null)
    {
          #Failed retriving Web Choice
          return $null
    }

    $IT_URL = $DefaultDiagURL
    if(!($IT_WebChoice -eq "Internet"))
    {
        $IT_URL = Get-DiagInput -ID "IT_URL"
        if($IT_URL -eq $null) {
          #Failed retriving URL
          return $null
        }

        #verify that it is a valid URL
        $validURL = GetValidURL $IT_URL[0]
        while($validURL -eq $null)
        {
                #build the RTF text
                $replacedError = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_InvalidURL_FormatError, $IT_URL[0]);
                $RTFText = GetErrorRTF ($localizationString.interaction_InvalidURL_Desc) ($replacedError);

                #reprompt for input
                $IT_URL = Get-DiagInput -ID "IT_Invalid_URL" -p @{"URL" = $IT_URL; "RTFText" = $RTFText}
                if($IT_URL -eq $null) {
                      #Failed retriving URL
                      return $null
                }

                $validURL = GetValidURL $IT_URL[0]
        }
    }

    return GetWebNDFIncidentData $validURL $false
}

function IsUNCFormat($UNC)
{
     &{
        $uri = [System.URI]($UNC);
        $scheme = $uri.scheme;
        if(($scheme -eq "file" ))
        {
            if($uri.IsUnc)
            {
                return $uri.LocalPath;
            }
        }
        return $null;
    }
     trap [Exception]
    {
        return $null;
    }
}

#function assumes passed in UNC is in \\host\share form (share can be missing)
function ContainsInvalidUNCChars($UNC)
{
    &{
        #will return an exception if the string has invalid characters
        $ignoreResult = [System.IO.Path]::IsPathRooted($UNC)

        #check the path for invalid characters
        #remove the starting slashes
        $tmp = $UNC.Substring(2)
        $nextSlash = $tmp.IndexOf("\")
        if(($nextSlash -lt 0) -or ($nextSlash -eq ($nextSlash.Length - 1)))
        {
            #string only contains hostname
            #hostname is already validated in IsUNCFormat function
            return $false
        }
        #remove host and backslash after host
        $UNCPath = $tmp.Substring($nextSlash+1)

        #under certain circumstances some of these make it through the above check
        #so we do a direct consistency check here
        if(!($UNCPath.IndexOfAny(@('/',':','*','?','"','<','>','|')) -eq -1))
        {
            return $true;
        }

        return $false;
    }
    trap [Exception]
    {
        return $true;
    }
}

function GetValidUNC($CandidateUNC)
{
    $toReturn = $null

    #is it valid
    $unc = IsUNCFormat $CandidateUNC
    if($unc)
    {
        $invalidChars = ContainsInvalidUNCChars $unc
        if($invalidChars)
        {
            $toReturn = -1;
        }
        else
        {
            $toReturn = $unc
        }
    }

    return $toReturn;
}


function GetUNCNDFIncidentData($UNC)
{
    #build entry point parameters
    $haXML = "<HelperAttributes><HelperAttribute><Name>UNCPath</Name><Type>AT_STRING</Type><Value><![CDATA[" + $UNC +  "]]></Value></HelperAttribute></HelperAttributes>"
    return @{"HelperClassName" = "SMBHelperClass"; "HelperAttributes" =$haXML}
}

function FileSharingEntry()
{
    $IT_UNC = Get-DiagInput -ID "IT_UNC"
    if($IT_UNC -eq $null) {
      #Failed retriving UNC path
      return $null
    }

    #assign input to non-array variable to facilitate usage and transform
    $validUNC = GetValidUNC $IT_UNC[0]
    while((!$validUNC) -or ($validUNC -eq -1))
    {
        #build the RTF text
        #use original entry for re-prompt even though "file://" UNC may have been transformed
        $replacedError = "";
        if(!$validUNC)
        {
            $replacedError = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_InvalidUNC_FormatError, $IT_UNC[0]);
        }
        else
        {
            $replacedError = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_InvalidUNC_CharError, $IT_UNC[0]);
        }
        $RTFText = GetErrorRTF ($localizationString.interaction_InvalidUNC_Desc) ($replacedError);

        #reprompt for input
        $IT_UNC = Get-DiagInput -ID "IT_Invalid_UNC" -p @{"UNC" = $IT_UNC; "RTFText" = $RTFText}
        if($IT_UNC -eq $null) {
              #Failed retriving UNC path
              return $null
        }

        $validUNC = GetValidUNC $IT_UNC[0]
    }

    return GetUNCNDFIncidentData $validUNC
}

function NetworkAdapterEntry()
{
    #enumerate interfaces to build options list
    $interfaces = get-wmiobject -class Win32_NetworkAdapter
    #hash table with options
    $optionList = @()
    foreach($curInterface in $interfaces)
    {
        if($curInterface.GUID -ne $null)
        {
              $curHash = @{"Name"=$curInterface.NetConnectionID}
              $curHash += @{"Description"=$curInterface.NetConnectionID}
              $curHash += @{"Value"=$curInterface.GUID}

              $optionList += @($curHash)
        }
    }

    if($optionList.Count -gt 1)
    {
        #add zero guid entry to check all interfaces
        $optionList += @(@{"Name"=$localizationString.interaction_AllAdapters; "Description"=$localizationString.interaction_AllAdapters; "Value"="{00000000-0000-0000-0000-000000000000}"; "ExtensionPoint"="<Default />"})

        #get interface selection from user
        $IT_NetworkAdapter = Get-DiagInput -ID "IT_NetworkAdapter" -c $optionList

        if($IT_NetworkAdapter -eq $null) {
           throw "Failed retriving Network Connetion ID from user"
        }
    }
    elseif($optionList.Count -eq 1)
    {
        $IT_NetworkAdapter = $optionList[0]["Value"]
    }
    else
    {
        #No NICs, do zero GUID diag
        $IT_NetworkAdapter = "{00000000-0000-0000-0000-000000000000}"
    }

    #build entry point parameters
    $haXML = "<HelperAttributes><HelperAttribute><Name>guid</Name><Type>AT_GUID</Type><Value>" + $IT_NetworkAdapter +  "</Value></HelperAttribute></HelperAttributes>"
    return @{"HelperClassName" = "NetConnection"; "HelperAttributes" =$haXML}
}

function WinsockEntry()
{
    $IT_RemoteAddress = Get-DiagInput -ID "IT_RemoteAddress"
    if($IT_RemoteAddress -eq $null -or  $IT_RemoteAddress[0].Length -eq 0) {
      #Failed retriving Remote Address
      return $null
    }

    $IT_Protocol = Get-DiagInput -ID "IT_Protocol"
    if($IT_Protocol -eq $null -or  $IT_Protocol[0].Length -eq 0) {
      #Failed retriving Remote Port
      return $null
    }

    $IT_ApplicationID = Get-DiagInput -ID "IT_ApplicationID"
    if($IT_ApplicationID -eq $null -or  $IT_ApplicationID[0].Length -eq 0) {
      #Failed retriving Application ID
      return $null
    }

    #build entry point parameters
    $haXML = "<HelperAttributes><HelperAttribute><Name>remoteaddr</Name><Type>AT_SOCKADDR</Type><Value>" + $IT_RemoteAddress  +  "</Value></HelperAttribute>";
    $haXML += "<HelperAttribute><Name>protocol</Name><Type>AT_UINT32</Type><Value>" + $IT_Protocol +  "</Value></HelperAttribute>";
    $haXML += "<HelperAttribute><Name>localaddr</Name><Type>AT_SOCKADDR</Type><Value>0.0.0.0</Value></HelperAttribute>";
    $haXML += "<HelperAttribute><Name>appid</Name><Type>AT_STRING</Type><Value>" + $IT_ApplicationID + "</Value></HelperAttribute>";
    $haXML += "</HelperAttributes>";
    return @{"HelperClassName" = "Winsock"; "HelperAttributes" =$haXML}
}

function GroupingEntry()
{
    $IT_GroupName = Get-DiagInput -ID "IT_GroupName"
    if($IT_GroupName -eq $null -or  $IT_GroupName[0].Length -eq 0) {
      #Failed retriving Remote Address
      return $null
    }

    #build entry point parameters
    $haXML = "<HelperAttributes><HelperAttribute><Name>groupname</Name><Type>AT_STRING</Type><Value>" + $IT_GroupName +  "</Value></HelperAttribute></HelperAttributes>"
    return @{"HelperClassName" = "GroupingHelperClass"; "HelperAttributes" =$haXML}
}

function GetValidExePath($File)
{
     &{
        $uri = [System.URI]($File);
        $scheme = $uri.scheme;
        if(($scheme -eq "file" ))
        {
            #make sure it send in .exe
            if($File.ToLower().IndexOf(".exe") -eq ($File.Length - 4))
            {
                return $File;
            }
        }
        return $null;
    }
    trap [Exception]
    {
        return $null;
    }
}

function InboundEntry()
{
    $staticOptionRes = @($INBOUND_FILESHARE_RESOURCE, $INBOUND_REMOTEDESKTOP_RESOURCE, $INBOUND_DISCOVERY_RESOURCE)
    $staticOptions = @($INBOUND_FILESHARE_PARAM, $INBOUND_REMOTEDESKTOP_PARAM, $INBOUND_DISCOVERY_PARAM)
    # If defined for the corresponding option, the item will be filtered out if the current sku matches anything in the list
    # Sku values as defined in the OperatingSystemSKU property of Win32_OperatingSystem
    $SKUFilters = @($null, @(2,3,5,11), $null)

    #get the SKU, to filter out inappropriate static options
    $SKUObject = get-wmiobject -class Win32_OperatingSystem -property "OperatingSystemSKU"
    $SKU = $SKUObject.OperatingSystemSKU

    $optionList = @()
    $curOptionIndex = 0
    for($curStaticOption = 0; $curStaticOption -lt $staticOptions.Length; $curStaticOption++)
    {
        $SKUFilter = $SKUFilters[$curStaticOption]
        if($SKUFilter)
        {
            if($SKUFilter -contains $SKU)
            {
                #should filter out this option from the list because it is not present in the SKU
                continue;
            }
        }

        $curApp = LoadResourceString($staticOptionRes[$curStaticOption])
        $curHash = @{}
        $curHash.Add("Name",$curApp)
        $curHash.Add("Value",$curOptionIndex)
        $curHash.Add("Description",$curApp)
        $curHash.Add("HelperAttributeName","serviceid")
        $curHash.Add("HelperAttributeValue",$staticOptions[$curStaticOption])
        $optionList += $curHash
        $curOptionIndex++
    }

    #add dynamic options (do not fail if call fails)
    $script:ExpectingException = $true
    
    $dll = "NetworkDiagnosticSnapIn.dll"

    try
    {
        RegSnapin $dll
        
        $droppedApps = [Microsoft.Windows.Diagnosis.Network.FirewallApi.ManagedMethods]::GetDiagnosticAppInfo()
        $script:ExpectingException = $false
        if($droppedApps)
        {
            foreach($droppedApp in $droppedApps)
            {
                #omit svchosts since we cannot display a friendly name for them
                if($droppedApp.Path.IndexOf("svchost") -eq -1)
                {
                    $appEntryDisplayStr = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_Inbound_Exe, $droppedApp.FriendlyName);
                    $curHash = @{}
                    $curHash.Add("Name",$appEntryDisplayStr)
                    $curHash.Add("Value",$curOptionIndex)
                    $curHash.Add("Description",$droppedApp.FriendlyName)
                    $curHash.Add("HelperAttributeName","appid")
                    $curHash.Add("HelperAttributeValue",$droppedApp.Path)
                    $optionList += $curHash
                    $curOptionIndex++
                }
            }
        }
    }
    finally
    {
        UnregSnapin $dll
    }

    #add the last option to browse for files
    $curApp = LoadResourceString($INBOUND_OTHER_RESOURCE)
    $curHash = @{}
    $curHash.Add("Name",$curApp)
    $curHash.Add("Value",$curOptionIndex)
    $curHash.Add("Description",$curApp)
    $curHash.Add("HelperAttributeName","serviceid")
    $curHash.Add("HelperAttributeValue",$INBOUND_OTHER_RESOURCE)
    $optionList += $curHash


    #trap exception if it happens, and if expected don't fail
    trap [Exception]
    {
        if($script:ExpectingException)
        {
            $script:ExpectingException = $false
            "Exception: " + $_.Exception.GetType().FullName + " Message: " + $_.Exception.Message  | convertto-xml | Update-DiagReport -id GetDiagAppInfoFailure -name "GetDiagAppInfo" -verbosity Debug
            continue;
        }
        else
        {
            #rethrow exception
            throw $_.Exception;
        }
    }

    $IT_ServiceName = Get-DiagInput -ID "IT_ServiceName" -c $optionList
    if($IT_ServiceName -eq $null -or  $IT_ServiceName[0].Length -eq 0) {
      #Failed retriving service name
      return $null
    }

    $optionSelected = $optionList[$IT_ServiceName]
    $optionSelected = $optionSelected[0] #need to to this to get access to the dictionary entry object
    $HelperAttributeName = $null
    $HelperAttributeValue = $null

    if($optionSelected.HelperAttributeValue -eq $INBOUND_OTHER_RESOURCE)
    {
        #show the file browsing interaction so that the user can pick their own executable
        $IT_BrowseFile = Get-DiagInput -ID "IT_BrowseFile"
        if($IT_BrowseFile -eq $null) {
          #Failed retriving file
          return $null
        }

        $validExe = GetValidExePath $IT_BrowseFile[0]
        while(!$validExe)
        {
            #build the RTF text
            #build the error
            $replacedError = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_InvalidExe_FormatError , $IT_BrowseFile[0]);
            #only a single line
            $RTFText = GetErrorRTF ($localizationString.interaction_InvalidExe_Desc) ($replacedError);

            #reprompt for input
            $IT_BrowseFile = Get-DiagInput -ID "IT_Invalid_BrowseFile" -p @{"File" = $IT_BrowseFile[0]; "RTFText" = $RTFText}
            if($IT_BrowseFile -eq $null) {
              #Failed retriving file
              return $null
            }

            $validExe = GetValidExePath $IT_BrowseFile[0]
        }


        $HelperAttributeName = "appid"
        $HelperAttributeValue = $IT_BrowseFile;
    }
    else
    {
        $HelperAttributeName = $optionSelected.HelperAttributeName
        $HelperAttributeValue = $optionSelected.HelperAttributeValue
    }

    #build entry point parameters
    $haXML = "<HelperAttributes>"
    $haXML += "<HelperAttribute><Name>" + $HelperAttributeName + "</Name><Type>AT_STRING</Type><Value>" + $HelperAttributeValue +  "</Value></HelperAttribute>"
    $haXML += "<HelperAttribute><Name>localaddr</Name><Type>AT_SOCKADDR</Type><Value>0.0.0.0</Value></HelperAttribute>"
    $haXML += "</HelperAttributes>"
    return @{"HelperClassName" = "Winsock"; "HelperAttributes" =$haXML}
}

function DirectAccessEntry()
{
    $toReturn = $null;

    $path = "hklm:SOFTWARE\Policies\Microsoft\Windows\NetworkConnectivityStatusIndicator\CorporateConnectivity";
    if(test-path $path)
    {
        $corpReg = get-itemproperty -path $path
        if($corpReg.WebProbeURL -and $corpReg.WebProbeURL.Length -gt 0)
        {
            #build entry point parameters
            $haXML = "<HelperAttributes><HelperAttribute><Name>URL</Name><Type>AT_STRING</Type><Value>" + $corpReg.WebProbeURL +  "</Value></HelperAttribute></HelperAttributes>"
            $toReturn = @{"HelperClassName" = "WinInetHelperClass"; "HelperAttributes" =$haXML}
        }
        elseif($corpReg.DnsProbeHost -and $corpReg.DnsProbeHost  -gt 0)
        {
            #build entry point parameters
            $haXML = "<HelperAttributes><HelperAttribute><Name>QueryName</Name><Type>AT_STRING</Type><Value>" + $corpReg.DnsProbeHost +  "</Value></HelperAttribute></HelperAttributes>"
            $toReturn = @{"HelperClassName" = "DnsHelperClass"; "HelperAttributes" =$haXML}
        }
    }
    return $toReturn;
}

function DefaultConnectivityFollowUpEntry()
{
    $toReturn = $null

    $IT_DefaultConnectivityInitialChoice = Get-DiagInput -ID "IT_DefaultConnectivityInitialChoice"
    if($IT_DefaultConnectivityInitialChoice -eq $null -or  $IT_DefaultConnectivityInitialChoice[0].Length -eq 0)
    {
      #Failed retriving service name
      return $null
    }

    #clear the progress so that the last step doesn't show before things get started again
    Write-DiagProgress -activity " "

    if($IT_DefaultConnectivityInitialChoice -eq "Other")
    {
        #query which other entry point they wish to use
        $IT_DefaultConnectivityOtherChoice = Get-DiagInput -ID "IT_DefaultConnectivityOtherChoice"
        if($IT_DefaultConnectivityOtherChoice[0].Length -eq 0)
        {
          #Failed retriving service name
          return $null
        }

        if($IT_DefaultConnectivityOtherChoice -eq "Inbound")
        {
            $toReturn = InboundEntry
        }
        elseif($IT_DefaultConnectivityOtherChoice -eq "DirectAccess")
        {
            $toReturn = DirectAccessEntry
            if(!$toReturn)
            {
                #not provisioned, root cause outside of NDF
                Update-DiagRootCause -ID "{E42E5B5A-16E0-43f1-AB32-C94C608D269D}" -Detected $true
                return;
            }
        }
        elseif($IT_DefaultConnectivityOtherChoice -eq "NetworkAdapter")
        {
            $toReturn = NetworkAdapterEntry
        }
    }
    else
    {
        #query for the URL/UNC path
        $IT_URLOrUNC = Get-DiagInput -ID "IT_URLOrUNC"
        $validUNC = $null
        #Is it a valid URL?
        $validURL = GetValidURL $IT_URLOrUNC[0]
        if(!$validURL)
        {
            $validUNC = GetValidUNC $IT_URLOrUNC[0]
        }

        while((!$validURL) -and
                    ((!$validUNC) -or ($validUNC -eq -1)))
        {

            #build the RTF text
            $replacedError = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.interaction_InvalidURLOrUNC_FormatError, $IT_URLOrUNC[0]);
            $RTFText = GetErrorRTF ($localizationString.interaction_InvalidURLOrUNC_Desc) ($replacedError);

            #reprompt for input
            $IT_URLOrUNC = Get-DiagInput -ID "IT_Invalid_URLOrUNC" -p @{"URLOrUNC" = $IT_URLOrUNC; "RTFText" = $RTFText}
            if($IT_URLOrUNC -eq $null) {
                  #Failed retriving URL/UNC path
                  return $null
            }

            $validURL = GetValidURL $IT_URLOrUNC[0]
            if(!$validURL)
            {
                $validUNC = GetValidUNC $IT_URLOrUNC[0]
            }
        }

        if($validURL)
        {
            $toReturn = GetWebNDFIncidentData $validURL $false
        }
        else
        {
             $toReturn = GetUNCNDFIncidentData $validUNC
        }
    }

    return $toReturn
}

function GetRepair($RepairRank, $RCEnum)
{
    $RCEnum.Reset()
    $rootCauseCount = $RCEnum.Count
    for($i=0; $i -lt $rootCauseCount; $i++)
    {
        $rootCause = $RCEnum.Next;
        $repairEnum = $rootCause.Repairs
        $repairCount = $repairEnum.Count;
        for($r=0; $r -lt $repairCount; $r++)
        {
            $curRep = $repairEnum.Next
            if($curRep.Rank -eq $RepairRank)
            {
                return $curRep
            }
        }
    }
    return $null
}

function SplitString($FullString, [ref]$Title, [ref]$Rest)
{
     $newLineIndex = $FullString.IndexOf("`n");
     if(!($newLineIndex -eq -1))
     {
         $Title.value = $FullString.Substring(0, $newLineIndex)
         $Rest.value = $FullString.Substring($newLineIndex+1)
     }
     else
     {
        $Title.value = $FullString
        $Rest.value = ""
     }
}

function GetTitleAndDesc($RCorRep, [ref]$Title, [ref]$Desc)
{
    $descEx = $RCorRep.DescriptionEx
    if(!($descEx -eq $null))
    {
        $Title.value = $descEx.Title
        $Desc.value = $descEx.Description
        if($Desc.value -eq $null)
        {
            $Desc.value = ""
        }
    }
    else
    {
        #split the string into title and description using \n
        SplitString $RCorRep.Description ($Title) ($Desc)
    }
}

function GetButtonParams($Repair, [ref]$Params, $DefaultName, $DefaultDesc, $ExtensionName, $ButtonITName, $ButtonITDesc)
{
    #defaults
    $buttonName = $DefaultName;
    $buttonDesc = $DefaultDesc;

    $descriptionEx = $Repair.DescriptionEx;

    if($descriptionEx)
    {
        #this is a XML based root cause, get replacement parameters
        $paramEnum = $DescriptionEx.Extensions;
        $paramCount = $paramEnum.Count;
        $paramEnum.Reset()
        for($curP=0;$curP -lt $paramCount; $curP++)
        {
            $param = $paramEnum.Next;
            if($param.Name -eq $ExtensionName)
            {
                SplitString $param.Value ([ref]$buttonName) ([ref]$buttonDesc);
                break;
            }
        }
    }

    $Params.value.Add($ButtonITName, $buttonName);
    $Params.value.Add($ButtonITDesc, $buttonDesc);
}

function GetContinueButtonParams($Repair, [ref]$Params)
{
    GetButtonParams $Repair $Params $localizationString.interaction_DefaultContinueButtonName $localizationString.interaction_DefaultContinueButtonDescription "ContinueButtonText" "IT_P_ContinueButtonName" "IT_P_ContinueButtonDescription"
}

function GetLaunchButtonParams($Repair, [ref]$Params)
{
    GetButtonParams $Repair $Params $localizationString.interaction_DefaultLaunchButtonName "" "LaunchButtonText" "IT_P_LaunchButtonName" "IT_P_LaunchButtonDescription"
}

function GetUnmanifestedRootCause($RootCause, [ref]$Params, $catchAllRC)
{
    #Get name and description of root cause
    $Name = "";
    $Desc = "";
    #don't use DescriptionEx for unmanifested root causes
    SplitString $RootCause.Description ([ref]$Name) ([ref]$Desc)
    #add to outgoing param list
    $Params.value.Add("RootCauseName", $Name);
    $Params.value.Add("RootCauseDescription", $Desc);

    #check whether repairs require elevation of not
    $LUARepairs = @();
    $elevateRepairs = @();
    $localRepairs = @();

    $repairEnum = $rootCause.Repairs
    $repairEnum.Reset()
    for($i=0; $i -lt $repairEnum.Count; $i++)
    {
        $repair = $repairEnum.Next

        if($catchAllRC -and !($repair.Flags -band $RF_RESERVED_CA))
        {
            #this is a catch all RC treatment, so we should skip the non-catch all repairs
            continue;
        }
        elseif(!$catchAllRC -and ($repair.Flags -band $RF_RESERVED_CA))
        {
            #this is not a catch all RC treatment, so we should skip catch all repairs
            continue;
        }

        $requiredSid = $Repair.RequiredSidType;
        if($requiredSid -eq $null)
        {
            throw "Could not retrieve required repair SID"
        }
        elseif(($requiredSid -eq  $WinBuiltinAdministratorsSid) -or ($requiredSid -eq  $WinBuiltinNetworkConfigurationOperatorsSid))
        {
            $elevateRepairs += @($repair);
        }
        elseif($requiredSid -eq  $WinLocalLogonSid)
        {
            $localRepairs += @($repair);
        }
        else
        {
            $LUARepairs += @($repair);
        }
    }

    $LUACount = $LUARepairs.Length;
    $elevateCount = $elevateRepairs.Length;
    $localCount = $localRepairs.Length;

    if($elevateCount + $LUACount -gt 2)
    {
        #currently we don't support more than two repairs for
        #unmanifested repairs.
        "LUA: " + $LUACount + " Elevation: " + $elevateCount | convertto-xml | Update-DiagReport -id UnmanifestedRootCause -name "Unmanifested Root Cause" -description "Unsupported number of total repairs for unmanifested root cause." -verbosity Debug
        return $null;
    }
    elseif($localCount -and ($elevateCount -or $LUACount))
    {
        #we don't support a combination of Local repairs with any other kind of repair
        "LUA: " + $LUACount + " Elevation: " + $elevateCount | convertto-xml | Update-DiagReport -id UnmanifestedRootCause -name "Unmanifested Root Cause" -description "Unsupported repair combination including Local repair." -verbosity Debug
        return $null;
    }
    elseif($localCount -gt 1)
    {
        #we don't support more than one Local repair
        "Local: " + $localCount | convertto-xml | Update-DiagReport -id UnmanifestedRootCause -name "Unmanifested Root Cause" -description "Unsupported number of total Local repairs." -verbosity Debug
        return $null;
    }

    #populate the parameter data.
    #NOTE: LUA repairs always are listed before
    #          elevation repairs (makes manifesting easier)
    $repairCount = $null;

    #guarantees that LUA repairs are placed before elevated
    $allRepairs = $LUARepairs + $elevateRepairs + $localRepairs;

    #lua repairs
    for($i=0; $i -lt $allRepairs.Length; $i++, $repairCount++)
    {
        $repair = $allRepairs[$i];
        #get name and description of repair
        #don't use DescriptionEx for unmanifested root causes
        SplitString $repair.Description ([ref]$Name) ([ref]$Desc)
        #add to outgoing param list
        $Params.value.Add("RepairName"+$repairCount, $Name);
        $Params.value.Add("RepairDescription"+$repairCount, $Desc);
        $Params.value.Add("RepairID"+$repairCount, $repair.RepairID);
    }

    #add security boundary safe data
    $data = GetSBSData($Global:ndf.IncidentID)
    $params.value.Add("SBSData", $data)

    #keywords for escalation
    $keywords = GetKeywords($RootCause.DescriptionEx);
    if($keywords.Length -gt 0)
    {
        $params.value.Add("Keywords", $keywords);
    }

    if(($LUACount -eq 1) -and ($elevateCount -eq 1))
    {
        #return placeholder RC with mix of admin and LUA repairs
        return "{000000000-0000-0000-0000-000000000005}"
    }
    elseif($elevateCount -eq 1)
    {
        #return placeholder RC with single elevation repair
        return "{000000000-0000-0000-0000-000000000002}"
    }
    elseif($elevateCount -eq 2)
    {
        #return placeholder RC with two Admin repairs
        return "{000000000-0000-0000-0000-000000000004}"
    }
    elseif($LUACount -eq 1)
    {
        #check whether the single repair is info only or help topic to use alternate root cause
        $repair = $LUARepairs[0];
        #########
        try { $uiInfo = $repair.UiInfo; } catch{ $uiInfo = $null }
        
        $repairFlags = $repair.Flags;

        if(($uiInfo -and ($uiInfo.Type -eq $UIT_HELP_PANE)) -or ($repairFlags -band $RF_INFORMATION_ONLY))
        {
            #return placeholder RC with single info only or help topic repair
            return "{000000000-0000-0000-0000-000000000006}"
        }
        else
        {
            #return placeholder RC with single LUA repair
            return "{000000000-0000-0000-0000-000000000001}"
        }
    }
    elseif($LUACount -eq 2)
    {
        #return placeholder RC with two LUA repairs
        return "{000000000-0000-0000-0000-000000000003}"
    }
    elseif($localCount -eq 1)
    {
        #return placeholder RC with a single Local user repair
        return "{000000000-0000-0000-0000-000000000007}"
    }

    return $null;
} 

function CreateCab($FileList, $TargetFolder, $TargetCAB)
{

$ddf = @"
.OPTION EXPLICIT
.Set CabinetNameTemplate=$TargetCAB
.set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set UniqueFiles=`"OFF`"
.Set Cabinet=on
.Set DiskDirectory1=$TargetFolder
$($OFS="`r`n"; $FileList)
"@;

    $ddfFile = "NetworkConfiguration.ddf";

    $ddf | Out-File $ddfFile -Encoding Ascii;
    makecab /f $ddfFile;
    $succeeded = $?;
    #del $ddfFile;

    return $succeeded;
}

function HereString($text)
{
    $here = "@'`n" + $text + "`n'@"
    return $here
}

#$Commands is an array of hash pairs  "command": the command line to run, "file": the target filename,
#appended to the end of the command line
function AddCommandOutputToSession($Commands, $TargetCABName, $ReportHeader)
{
    #lets create the temporary folder for all this data
    $tempFolder = [System.IO.Path]::GetTempFileName();
    del $tempFolder; #delete the file
    mkdir $tempFolder; #make it into a folder

    #go into the folder to avoid leaving side-effect files behind
    pushd $tempFolder;

    #run the commands in the list
    $fileList = @();
    $timeMeasure = @(); #keeps track of length of execution for commands
    foreach($item in $Commands)
    {
        #measure time it takes to execute commands
        $start = get-date

        $targetFile = $tempFolder + "\" + $item["file"];
        $cmdline = $item["command"] + " " + (HereString $targetFile);
        $err = $($out = Invoke-expression  $cmdline) 2>&1;
        if(!$err)
        {
            #the call succeeded, add the target file to the list to CAB
            $fileList += @($item["file"]);
        }
        $runtime = (get-date) - $start;
        $timeMeasure += @(@{"command"=$item["command"];"runtime (ms)"=$runtime.TotalMilliseconds});
    }

    #create a CAB of the files
    $start = get-date
    if(CreateCab ($fileList) (".\") ($TargetCABName))
    {
        $runtime = (get-date) - $start;
        $timeMeasure += @(@{"command"="makecab.exe";"runtime (ms)"=$runtime.TotalMilliseconds});
        Update-DiagReport -ID NetworkData -name $ReportHeader -File ($tempFolder + "\"+ $TargetCABName)
    }

    $timeMeasure | convertto-xml | Update-DiagReport -ID ConfigCollectionRuntime -Name "Data Collection Time" -Verbosity Debug

    popd;

    remove-item -recurse -force $tempFolder;
}

function AddNetworkInfoToSession()
{
    Write-DiagProgress -activity $localizationString.progress_Collecting_Config

    $commands = @(
        @{"command"="ipconfig /all >"; "file"="ipconfig.all.txt"};
        @{"command"="route print >"; "file"="route.print.txt"};
    );

    AddCommandOutputToSession ($commands) ("NetworkConfiguration.cab") ($localizationString.OtherNetworkConfigReportName);

    Write-DiagProgress -activity " "
}

function GetUniqueFileName($IncidentID, $Operation)
{
    #get temp folder location
    $tempFolder = get-childitem -path env:temp

     #strip { } at the ends of the incident ID GUID, generate file name
     $uniqueFileName = $tempFolder.Value+"\"+$IncidentID.Substring(1,$IncidentID.Length-2)+"." + $Operation

    #append whether it's admin or not (to avoid name clashes, as op count resets after elevation)
    $isAdmin = IsAdmin
    if($isAdmin)
    {
        $uniqueFileName += ".Admin";
    }

    #initialize or increment op count
    if($Global:OpCount -eq $null)
    {
       $Global:OpCount = 0
    }
    else
    {
        $Global:OpCount++;
    }

    $uniqueFileName += "." + $Global:OpCount + ".etl";

    return $uniqueFileName
}

function AddTraceFileToSession($Ndf, $Name, $Operation)
{
    #NDF flushes the trace file every time we query for it
    #A unique name needs to be generated each time we add the file to the report,
    #otherwise it will overwrite the existing ETL file
    #The naming convention is as follows:
    #  IncidentID.Operation([Admin]).Counter.etl

    Write-DiagProgress -activity $localizationString.progress_Collecting_Logs

    $traceFile = $Ndf.TraceFile
    if($traceFile)
    {
        #get unique name
        $newFileName = GetUniqueFileName $Ndf.IncidentID $Operation

        #rename file
        move "$traceFile" "$newFileName"
        #add it to the report
        Update-DiagReport -ID NDFDebugLog -name $Name -File $newFileName
        #add HC events to the report
        AddNewHCEventsToSession $newFileName
        # delete the file name edited by Claton 
        #del "$newFileName"
    }
    else
    {
        $Name | convertto-xml | Update-DiagReport -id TraceFile -name "Trace File" -description "Failed while trying to retrieve the trace file for the session." -verbosity Debug
    }

    Write-DiagProgress -activity " "
}

function AddNewHCEventsToSession($TraceFile)
{
    #we keep track of all the events added to the report, so we don't add duplicates (this function is called multiple times)
    if($Global:ReportEvents -eq $null)
    {
        $Global:ReportEvents = @{};
    }

    $script:ExpectingException = $false

    &{
        $script:ExpectingException = $true
        $events = get-winevent -path $TraceFile -Oldest -FilterXPath "*[System[Provider[@Name='Microsoft-Windows-Diagnostics-Networking'] and (EventID=6100)]]" -ErrorAction SilentlyContinue
        $script:ExpectingException = $false
        foreach($event in $events)
        {
            #events indexed by time they were emitted
            if(($event -ne $null) -and !$Global:ReportEvents.ContainsKey($event.TimeCreated))
            {
                #Add helper class name to title so that it's easily distinguishable in the report without having to expand it
                $eventTitle = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture, $localizationString.HelperClassEventNameWithHCName,
                                    [System.Globalization.CultureInfo]::CurrentUICulture.TextInfo.ToTitleCase($event.Properties[0].Value));

                "<Objects><Object Type=""System.String""><PRE><![CDATA["+$event.Message +"]]></PRE></Object></Objects>" | Update-DiagReport -id DiagInformation -name $eventTitle
                $Global:ReportEvents.Add($event.TimeCreated, $event)
            }
        }
    }
    trap [Exception]
    {
        if($script:ExpectingException)
        {
            "No admin helper class events were found." | convertto-xml | Update-DiagReport -id DiagEvents -name "Helper Class Events" -verbosity Debug
        }
        else
        {
            "Exception: " + $_.Exception.GetType().FullName + " Message: " + $_.Exception.Message  | convertto-xml | Update-DiagReport -id DiagEventsFailure -name "Helper Class Events" -description "Failed while retrieving helper class events." -verbosity Debug
        }
        return
    }
}

function LoadResourceString($ResourceString)
{
    [string]$bufStr = $null
    $dll = "NetworkDiagnosticSnapIn.dll"

    try
    {
        RegSnapin $dll
        
        $bufferSize = 512
        $buffer = New-Object System.Text.StringBuilder $bufferSize
        [Microsoft.Windows.Diagnosis.Network.NativeShellMethods]::SHLoadIndirectString($ResourceString, $buffer, $bufferSize, [IntPtr]::Zero)
        $bufStr = $buffer.ToString()
    }
    finally
    {
        UnregSnapin $dll
    }

    return $bufStr
}

function IsDPSStarted()
{
    $dpsService = get-service "DPS"
    if($dpsService)
    {
        if($dpsService.Status -ne "Running")
        {
            return $false;
        }
    }
    return $true;
}

function IsDPSDisabled()
{
    $dpsService = gwmi win32_service -f "name='DPS'"
    if($dpsService)
    {
        if($dpsService.StartMode -eq "Disabled")
        {
            return $true;
        }
    }
    return $false;
}

function IsSafeMode()
{
    [void] [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    return [System.Windows.Forms.SystemInformation]::BootMode -ne 0
}

function IsHelpTopicAllowed($Link)
{
    $regValue = get-itemproperty -path hklm:\SYSTEM\CurrentControlSet\Control\NetDiagFx\Config\HelpTopic  -name $Link -ErrorAction SilentlyContinue -ErrorVariable regError
    if($regValue)
    {
        # check the DWORD value (the key to the value is the Value name: i.e., $Link) 
        # 1         - enabled
        # otherwise - disabled
        $filterValue = $regValue.$Link;
        if($filterValue -eq 1)
        {
            return $true;
        }
        else
        {
            return $false;
        }
    }
    elseif ($regError)
    {
        if(!($regError[0].CategoryInfo.Category -eq "InvalidArgument") -and  !($regError[0].CategoryInfo.Category -eq "ObjectNotFound"))
        {
            " Warning: Unexpected error when reading Help Topic Cause key : " + $regError[0].CategoryInfo.Category | convertto-xml | Update-DiagReport -id UnexpectedRegError -name "Unexpected Registry Error" -verbosity Debug
        }
        return $false;
    }
}
