Public/Start-SACPurge.ps1
|
function Start-SACPurge { <# .SYNOPSIS Enterprise Autodesk Master Purge Script .DESCRIPTION Hard-kills Autodesk services, uninstalls MSI and modern components, purges registry/file remnants, and cleans desktops. Handles locked files gracefully with dual-channel logging to prevent IO lock exceptions. Includes safe-evaluation regex removal of SQL Server LocalDB. Supports robust temporary pathing, falling back to $env:TEMP if C:\temp is unavailable. Creates an AttentionItems.txt log if any critical component failures are detected. Add/Remove Programs entries are evicted only after the product's Windows Installer registration is confirmed gone. An uninstaller that returns 3010 ends the run: the reboot has to complete the MSI transaction before anything else is removed. Supports -WhatIf for a non-destructive preview of every action. #> [CmdletBinding(SupportsShouldProcess)] param ( [string[]]$AdditionalVendors = @(), [switch]$AnyVendor, [switch]$ClearTemp, [switch]$Silent ) $StopWatch = [System.Diagnostics.Stopwatch]::StartNew() $IsPreview = [bool]$WhatIfPreference $script:SACFailures = @() $script:SACLockedItemsList = [System.Collections.Generic.List[string]]::new() $script:SACResidueReport = $null Reset-SACEvictionState # --- Configuration Arrays --- $RemoveVersions = @( @{Name = "AutoCAD"; Versions = @("*") }, @{Name = "Civil 3D"; Versions = @("*") }, @{Name = "Revit"; Versions = @("*") }, @{Name = "Autodesk"; Versions = @("*") } ) $ProcessesToKill = @("acad*", "AcEventSync*", "AcQMod*", "revit*", "*adsk*", "AdskAccess*", "GenuineService*", "*AdAppMgr*", "*AdODIS*", "*Autodesk*", "3dsmax*", "maya*", "inventor*", "roamer*", "navisworks*", "recap*", "dwgviewr*", "DesktopConnector*", "fusion*", "alias*", "vault*", "moldflow*", "modlflow*", "netfabb*") $DataLocations = @( "$($env:ProgramData)\Autodesk", "C:\ProgramData\Autodesk Navisworks Freedom *", "$($env:PUBLIC)\Documents\Autodesk", "C:\Users\*\AppData\Local\Autodesk", "C:\Users\*\AppData\Local\Autodesk Navisworks", "C:\Users\*\AppData\Local\Autodesk,_Inc", "C:\Users\*\AppData\Local\Autodesk Inc", "C:\Users\*\AppData\Local\Autodesk Installer", "C:\Users\*\AppData\Local\Autodesk Ltd", "C:\Users\*\AppData\Local\Autodesk Navisworks Freedom *", "C:\Users\*\AppData\Local\AdSSO", "C:\Users\*\AppData\Local\Civil3DFeatures", "C:\Users\*\AppData\Roaming\Autodesk", "C:\Users\*\AppData\Roaming\Autodesk Navisworks", "C:\Users\*\AppData\Roaming\Autodesk,_Inc", "C:\Users\*\AppData\Roaming\Autodesk Inc", "C:\Users\*\AppData\Roaming\Autodesk Installer", "C:\Users\*\AppData\Roaming\Autodesk Ltd", "C:\Users\*\AppData\Roaming\Autodesk Navisworks Freedom *", "C:\Users\*\AppData\Roaming\AdSSO", "C:\Users\*\AppData\Roaming\Civil3DFeatures", "C:\Users\*\AppData\Local\Temp\Autodesk", "$($env:ProgramFiles)\Autodesk", "$($env:CommonProgramFiles)\Autodesk Shared", "$($env:CommonProgramFiles)\Autodesk", "$(${env:ProgramFiles(x86)})\Autodesk", "$(${env:CommonProgramFiles(x86)})\Autodesk Shared", "$(${env:CommonProgramFiles(x86)})\Autodesk", "C:\Autodesk", "C:\Windows\Temp\AdAppMgrUpdater", "C:\Users\*\ACCDocs", "C:\Users\*\BIM 360" ) $RegistryLocations = @( "HKCU:\Software\Autodesk", "HKCU:\Software\Wow6432Node\Autodesk", "HKLM:\Software\Autodesk", "HKLM:\Software\Wow6432Node\Autodesk", "HKU:\*\Software\Autodesk", "HKU:\*\Software\Wow6432Node\Autodesk" ) # --- Logging Setup --- $ToDate = (Get-Date -Format 'yyyyMMdd_HHmmss') $BaseTemp = if (Test-Path "C:\temp") { "C:\temp" } else { $env:TEMP } $LogDir = Join-Path $BaseTemp "AutodeskPurge_$($ToDate)" # -WhatIf:$false - a preview is exactly when the audit trail matters, so the run # folder and transcript are always created. New-Item -ItemType Directory -Path $LogDir -Force -ErrorAction SilentlyContinue -WhatIf:$false | Out-Null $TranscriptLog = "$($LogDir)\PurgeTranscript.log" $DebugLog = "$($LogDir)\PurgeDebug.log" Start-Transcript -Path $TranscriptLog -Append -Force -WhatIf:$false | Out-Null if ($IsPreview) { Write-SACMsg " PREVIEW (-WhatIf): no changes will be made." "Warning" } # --- Helper Functions (Centralized) --- function Invoke-SACShortcutPurge { Write-SACMsg "Sweeping desktops and start menus for Autodesk shortcuts..." "Info" $ShortcutLocations = @( "$($env:PUBLIC)\Desktop", "C:\Users\*\Desktop", "C:\Users\*\OneDrive\Desktop", "$($env:ProgramData)\Microsoft\Windows\Start Menu\Programs", "C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs" ) $Shell = New-Object -ComObject WScript.Shell $AutodeskPath = "$($env:ProgramFiles)\Autodesk\" $AutodeskPathX86 = "$(${env:ProgramFiles(x86)})\Autodesk\" # Stricter name patterns for fallback $ShortcutPatterns = @("*AutoCAD*", "*Revit*", "*Autodesk*", "*Civil 3D*", "*BIM 360*", "*Recap*", "*Navisworks*", "*3ds Max*", "*Maya*", "*Inventor*") foreach ($loc in $ShortcutLocations) { # Resolve wildcards for user profiles $resolved = if ($loc -match '\*') { Resolve-Path $loc -ErrorAction SilentlyContinue } else { ,$loc } foreach ($path in $resolved) { if (Test-Path $path) { Get-ChildItem -Path $path -Filter "*.lnk" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object { $isMatch = $false $lnkBore = $_ $target = "" # --- STEP 1: Deep Inspection of Target Path (PRIORITY) --- try { $shortcut = $Shell.CreateShortcut($lnkBore.FullName) $target = $shortcut.TargetPath if (-not [string]::IsNullOrWhiteSpace($target)) { # If we HAVE a target path, the decision is based STRICTLY on that path. if ($target -like "$AutodeskPath*" -or $target -like "$AutodeskPathX86*") { $isMatch = $true } else { # EXPLICIT SAFETY: If it points elsewhere (e.g., PDQ Inventory), it is NOT a match. $isMatch = $false } } } catch { Write-SACQuietLog "Failed to inspect shortcut target for $($lnkBore.FullName)" } # --- STEP 2: Name-Based Fallback (Only if Target Path is empty/unresolvable) --- # Some advertised shortcuts (MSI) return empty TargetPath via COM. if (-not $isMatch -and [string]::IsNullOrWhiteSpace($target)) { foreach ($pattern in $ShortcutPatterns) { if ($lnkBore.Name -like "$pattern*") { $isMatch = $true break } } } if ($isMatch -and (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $lnkBore.FullName -Action 'Delete shortcut')) { try { Remove-Item $lnkBore.FullName -Force -ErrorAction Stop Write-SACMsg "Deleted shortcut: $($lnkBore.FullName)" "Success" } catch { Write-SACQuietLog "Failed to delete shortcut $($lnkBore.FullName): $($_.Exception.Message)" } } } } } } } function Stop-AndRemoveService { param ([string]$ServiceName) # The LIKE filter is deliberately broad, so every hit must also prove it belongs to # Autodesk before it is deleted: '%AGS%' on its own matches BTAGService, defragsvc # and diagsvc, none of which this tool has any business removing. $ServiceVendorPattern = 'Autodesk|Adsk|AdODIS|AdAppMgr|Genuine Service' $ServiceCim = Get-CimInstance Win32_Service -Filter "Name LIKE '%$($ServiceName)%' OR DisplayName LIKE '%$($ServiceName)%'" foreach ($svc in $ServiceCim) { $ServiceIdentity = @($svc.Name, $svc.DisplayName, $svc.PathName) -join ' ' if ($ServiceIdentity -notmatch $ServiceVendorPattern) { Write-SACQuietLog "Skipping non-Autodesk service matched by '$ServiceName': $($svc.Name)" continue } if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $svc.Name -Action 'Stop and delete service')) { continue } if ($svc.State -eq 'Running' -and $svc.ProcessId -gt 0) { Write-SACMsg "Hard killing process ID $($svc.ProcessId) for service $($svc.Name)" "Warning" try { Stop-Process -Id $svc.ProcessId -Force -ErrorAction Stop } catch { Write-SACQuietLog "Failed to kill PID $($svc.ProcessId): $($_.Exception.Message)" } } try { Set-Service -Name $svc.Name -StartupType Disabled -ErrorAction Stop sc.exe delete $svc.Name 2>&1 | Out-Null Write-SACMsg "Removed service: $($svc.Name)" "Success" } catch { Write-SACQuietLog "Failed to disable/remove service $($svc.Name): $($_.Exception.Message)" } } } function Invoke-RemoveODISAndLicensing { Write-SACMsg "Targeting modern Autodesk ODIS and Licensing components..." "Info" $UninstallerPaths = @( "$($env:ProgramFiles)\Autodesk\AdODIS\V1\RemoveODIS.exe", "$(${env:CommonProgramFiles(x86)})\Autodesk Shared\AdskLicensing\uninstall.exe", "$($env:ProgramFiles)\Autodesk\Autodesk AdSSO\uninstall.exe" ) foreach ($path in $UninstallerPaths) { if (Test-Path $path) { if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $path -Action 'Run native uninstaller')) { continue } Write-SACMsg "Executing native uninstaller: $($path)" "Info" try { $Process = Start-Process -FilePath $path -ArgumentList "--mode unattended" -PassThru -Wait -NoNewWindow -ErrorAction Stop Write-SACMsg "Uninstaller exited with code: $($Process.ExitCode)" "Info" } catch { Write-SACQuietLog "Failed to execute uninstaller $($path): $($_.Exception.Message)" } } } } function Invoke-RemoveSQLLocalDB { Write-SACMsg "Evaluating SQL Server LocalDB dependencies..." "Info" $AutodeskPatterns = @("*SteelConnections*", "*AdvanceSteel*", "*Revit*", "*AutoCAD*", "MSSQLLocalDB", "v11.0") $instances = try { & sqllocaldb info 2>$null } catch { $null } if ($instances) { $unknownInstances = @() foreach ($inst in $instances) { $isAutodesk = $false foreach ($pattern in $AutodeskPatterns) { if ($inst -like $pattern) { $isAutodesk = $true; break } } if (-not $isAutodesk) { $unknownInstances += $inst } } if ($unknownInstances.Count -gt 0) { Write-SACMsg "Found unknown LocalDB instances. Skipping SQL removal to prevent breaking other apps." "Warning" foreach ($inst in $unknownInstances) { Write-SACQuietLog "Skipping due to unknown instance: $inst" } return } Write-SACMsg "Only Autodesk/Default LocalDB instances detected. Proceeding with SQL purge..." "Success" foreach ($inst in $instances) { if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $inst -Action 'Stop and delete LocalDB instance')) { continue } Write-SACQuietLog "Stopping and deleting instance: $inst" & sqllocaldb stop "$inst" 2>&1 | Out-Null & sqllocaldb delete "$inst" 2>&1 | Out-Null } } else { Write-SACMsg "No active LocalDB instances found." "Info" } Write-SACMsg "Locating SQL Server LocalDB MSIs..." "Info" $LocalDbRegex = "^Microsoft SQL Server (2014|2019).*LocalDB" $regPaths = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ) $appsToUninstall = @() foreach ($path in $regPaths) { if (Test-Path $path) { Get-ChildItem -Path $path -ErrorAction SilentlyContinue | ForEach-Object { $dn = $_.GetValue("DisplayName") if ($null -ne $dn -and $dn -match $LocalDbRegex) { $appsToUninstall += [PSCustomObject]@{ DisplayName = $dn PSChildName = $_.PSChildName } } } } } if ($appsToUninstall) { foreach ($app in $appsToUninstall) { $guid = $app.PSChildName $name = $app.DisplayName if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $name -Action 'Uninstall SQL LocalDB')) { continue } Write-SACMsg "Uninstalling: $name" "Info" $MsiLogFile = "$($LogDir)\$($name -replace '[\\/:\*\?"<>\|]','')_Uninstall.log" $process = Start-Process -FilePath "msiexec.exe" -ArgumentList "/x $guid /qn /norestart REBOOT=ReallySuppress /L*v `"$($MsiLogFile)`"" -Wait -NoNewWindow -PassThru if ($process.ExitCode -eq 0) { Write-SACMsg "Successfully uninstalled $name." "Success" } else { Write-SACMsg "Uninstall for $name returned exit code $($process.ExitCode)." "Warning" } } } else { Write-SACQuietLog "Target SQL LocalDB installations not found in the registry." } $localDbAppData = "$env:LOCALAPPDATA\Microsoft\Microsoft SQL Server Local DB" if ((Test-Path $localDbAppData) -and (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $localDbAppData -Action 'Purge directory')) { Write-SACQuietLog "Purging residual LocalDB AppData..." Invoke-SACRobocopyPurge -TargetPath $localDbAppData | Out-Null } } function Invoke-UninstallAutodeskProduct { param ([string]$ProductName, [string[]]$Versions) foreach ($version in $Versions) { if (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target 'Autodesk processes and scheduled tasks' -Action 'Terminate and disable') { # Kill running processes before we start uninstallation foreach ($processName in $ProcessesToKill) { try { Get-Process -Name $processName -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction Stop } catch { Write-SACQuietLog "Could not stop process $($processName): $($_.Exception.Message)" } } # Disable Autodesk Scheduled Tasks FIRST to prevent processes from restarting Get-ScheduledTask -TaskPath "\Autodesk\*" -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue Get-ScheduledTask -TaskName "*Autodesk*" -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue } Write-SACMsg "Starting uninstallation sequence for $($ProductName) $($version)..." "Info" Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -Force -ErrorAction SilentlyContinue $PackageName = if ($version -eq "*") { "*$($ProductName)*" } else { "*$($ProductName)*$($version)*" } $vendorPattern = "^$" if (-not $AnyVendor) { $vendors = @("Autodesk") if ($AdditionalVendors) { $vendors += $AdditionalVendors } $vendorPattern = ($vendors | ForEach-Object { [regex]::Escape($_) }) -join '|' } $regPaths = @( 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' ) $UninstallKeys = @() foreach ($path in $regPaths) { if (Test-Path $path) { Get-ChildItem -Path $path -ErrorAction SilentlyContinue | ForEach-Object { $dn = $_.GetValue("DisplayName") $pb = $_.GetValue("Publisher") if ($null -ne $dn -and ($dn -like $PackageName -or ($ProductName -eq "Autodesk" -and $null -ne $pb -and $pb -match "Autodesk"))) { if ($AnyVendor -or ($null -ne $pb -and $pb -match $vendorPattern) -or ($dn -match $vendorPattern)) { $UninstallKeys += [PSCustomObject]@{ DisplayName = $dn Publisher = $pb UninstallString = $_.GetValue("UninstallString") QuietUninstallString = $_.GetValue("QuietUninstallString") InstallLocation = $_.GetValue("InstallLocation") PSChildName = $_.PSChildName PSPath = $_.PSPath } } } } } } $Tier2Pattern = 'Service Pack|\bSP\d\b|Hotfix|Patch|Update \d|Security Update' $Tier3Pattern = 'Language Pack|Object Enabler|Add-[Ii]n|Plugin|Extension|' + 'Content Library|Content Core|Content Essential|Content Basic|' + 'Material Library Base|Material Library Low|Material Library Medium|Material Library High|' + 'Sample|Template|Documentation|DWG TrueView|' + 'Colorbooks|Color Books|Unit Schemas|MEP Content|MEP Metric|MEP Imperial|' + 'Revit Content|Batch Print|eTransmit|Worksharing Monitor|DB Link|Model Review|' + 'BIM Interoperability|Cloud Models|Issues Addon|Robot Structural Analysis Extension|' + 'OpenStudio CLI' $Tier4Pattern = 'Shared Component|Collaboration for Revit|Desktop Connector|Desktop App|Single Sign|Autodesk Access|Autodesk Identity|Genuine Service' function Get-SACTier { param ([string]$DisplayName) if ($DisplayName -match $Tier2Pattern) { return 2 } if ($DisplayName -match $Tier3Pattern) { return 3 } if ($DisplayName -match $Tier4Pattern) { return 4 } return 1 } function Invoke-SACUninstallEntry { param ([object]$app) $ProductCode = $app.PSChildName $DisplayName = $app.DisplayName $QuietUninstallString = $app.QuietUninstallString $UninstallString = if (-not [string]::IsNullOrWhiteSpace($QuietUninstallString)) { $QuietUninstallString } else { $app.UninstallString } $MsiLogFile = "$($LogDir)\$($DisplayName -replace '[\\/:\*\?"<>\|]','')_Uninstall.log" if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $DisplayName -Action 'Uninstall')) { return } Write-SACMsg "Uninstalling: $DisplayName" "Warning" $Process = $null if ($ProductCode -match '^{.*}$') { # Robust MSI detection: case-insensitive, handles quotes and paths if ($UninstallString -match 'msiexec\.exe') { Write-SACMsg " [MSI] $DisplayName" "Info" $Process = Start-Process "msiexec.exe" -ArgumentList "/x $($ProductCode) /qn /norestart REBOOT=ReallySuppress MSIRESTARTMANAGERCONTROL=Disable /L*v `"$($MsiLogFile)`"" -PassThru -WindowStyle Hidden } else { Write-SACMsg " [Custom-MSI] $DisplayName" "Info" $Process = Invoke-SACCustomUninstall -App $app -UninstallString $UninstallString -DisplayName $DisplayName } } else { $Process = Invoke-SACCustomUninstall -App $app -UninstallString $UninstallString -DisplayName $DisplayName } $ExitCode = $null if ($null -ne $Process) { Watch-SACProcessTree -RootProcess $Process -DisplayName $DisplayName -TimeoutMinutes 20 -IdleTimeoutMinutes 5 -TailLogFile $MsiLogFile $ExitCode = $Process.ExitCode Write-SACMsg " Exit code: $($ExitCode) for $($DisplayName)" "Info" if ($ExitCode -ne 0 -and $ExitCode -ne 3010 -and $ExitCode -ne 1605) { $script:SACFailures += [PSCustomObject]@{ Component = "Uninstall: $DisplayName"; Reason = "Exit Code $($ExitCode)" } } } # The ARP key is only a projection of the installer hives. Evicting it while the # product is still registered (3010 pending reboot, or a failed 1603) hides a live # MSI registration from every ARP-driven tool, so the decision is made against the # registration itself. Invoke-SACArpEviction -App $app -ExitCode $ExitCode -Cmdlet $PSCmdlet | Out-Null } function Invoke-SACCustomUninstall { param ([object]$App, [string]$UninstallString, [string]$DisplayName) if ([string]::IsNullOrWhiteSpace($UninstallString)) { Write-SACQuietLog "No UninstallString found for $($DisplayName). Skipping." return $null } $ExePath = "" $Arguments = "" if ($UninstallString -match '^"([^"]+)"(.*)$') { $ExePath = $matches[1] $Arguments = $matches[2].Trim() } elseif ($UninstallString -match '^(.*\.exe)(.*)$') { $ExePath = $matches[1].Trim() $Arguments = $matches[2].Trim() } else { $ExePath = $UninstallString } if ([string]::IsNullOrWhiteSpace($ExePath)) { Write-SACQuietLog "Could not parse executable path from UninstallString for $($DisplayName). Skipping." return $null } # If it's MSIExec, we MUST avoid non-MSI flags like --silent or --mode $isMsi = ($ExePath -match 'msiexec\.exe') $FullArgs = if ($isMsi) { "$($Arguments) /qn /quiet /norestart".Trim() } else { "$($Arguments) --silent /qn /quiet /norestart --mode unattended".Trim() } try { # Use cmd /c with NUL redirection to discourage interactive prompts from hanging the process. # We still use Start-Process -PassThru to get the PID for the Supervisor. $cmdArgs = "/c `"`"$ExePath`" $FullArgs < NUL`"" Write-SACQuietLog "Executing custom uninstall for ${DisplayName}: cmd.exe $cmdArgs" return Start-Process -FilePath "cmd.exe" -ArgumentList $cmdArgs -PassThru -WindowStyle Hidden -ErrorAction Stop } catch { Write-SACQuietLog "Failed to execute custom uninstaller for $($DisplayName): $($_.Exception.Message)" Write-SACMsg " Execution failed for $($DisplayName) (See Debug Log)" "Error" $script:SACFailures += [PSCustomObject]@{ Component = "Uninstaller Execution: $DisplayName"; Reason = $_.Exception.Message } return $null } } $Classified = @() foreach ($app in $UninstallKeys) { $tier = Get-SACTier -DisplayName $app.DisplayName $Classified += [PSCustomObject]@{ Tier = $tier; App = $app } } $Tier1 = $Classified | Where-Object { $_.Tier -eq 1 } $Tier2 = $Classified | Where-Object { $_.Tier -eq 2 } $Tier3 = $Classified | Where-Object { $_.Tier -eq 3 } $Tier4 = $Classified | Where-Object { $_.Tier -eq 4 } $total = $Classified.Count $skippable = $Tier2.Count + $Tier3.Count Write-SACMsg "Found $total component(s) for Purge: $($Tier1.Count) primary, $skippable update/addon(s), $($Tier4.Count) shared." "Info" # --- Tier 1: Primary products (full uninstall) --- $tier1Succeeded = New-Object 'System.Collections.Generic.HashSet[string]' foreach ($item in $Tier1) { if ($script:SACRebootRequired) { break } Invoke-SACUninstallEntry -app $item.App $baseName = $item.App.DisplayName -replace $Tier2Pattern,'' -replace $Tier3Pattern,'' -replace $Tier4Pattern,'' -replace '\s+', ' ' $tier1Succeeded.Add($baseName.Trim()) | Out-Null } if ($script:SACRebootRequired) { return } # --- Tier 2: Service packs / updates --- foreach ($item in $Tier2) { if ($script:SACRebootRequired) { break } $dn = $item.App.DisplayName if ($tier1Succeeded.Count -gt 0) { Write-SACMsg " [SKIP uninstall] Parent removed - checking registration before eviction: $dn" "Info" Invoke-SACArpEviction -App $item.App -Cmdlet $PSCmdlet | Out-Null } else { Write-SACMsg " [FULL uninstall] No parent removed - running uninstaller: $dn" "Info" Invoke-SACUninstallEntry -app $item.App } } if ($script:SACRebootRequired) { return } # --- Tier 3: Add-ons / extensions --- foreach ($item in $Tier3) { if ($script:SACRebootRequired) { break } $dn = $item.App.DisplayName if ($tier1Succeeded.Count -gt 0) { Write-SACMsg " [SKIP uninstall] Parent removed - checking registration before eviction: $dn" "Info" Invoke-SACArpEviction -App $item.App -Cmdlet $PSCmdlet | Out-Null } else { Write-SACMsg " [FULL uninstall] No parent removed - running uninstaller: $dn" "Info" Invoke-SACUninstallEntry -app $item.App } } if ($script:SACRebootRequired) { return } # --- Tier 4: Shared components (always full uninstall, last) --- foreach ($item in $Tier4) { if ($script:SACRebootRequired) { break } Invoke-SACUninstallEntry -app $item.App } if ($script:SACRebootRequired) { return } } } function Complete-SACPurgeRun { param ([switch]$Halted) # Entries whose eviction was refused earlier may have become evictable once the # installer-cache purge ran. Never retried when a reboot is outstanding. if (-not $Halted) { Invoke-SACDeferredArpEviction -Cmdlet $PSCmdlet } # Verification reads the installer hives, not Add/Remove Programs. An ARP-driven # rescan cannot see a product it has just orphaned. Write-SACMsg "Verifying Windows Installer hives for orphaned registrations..." "Info" $Residue = Get-SACInstallerResidue $script:SACResidueReport = Write-SACInstallerResidueReport -Residue $Residue -LogDir $LogDir foreach ($orphan in $Residue.OrphanedProducts) { $label = if ([string]::IsNullOrWhiteSpace($orphan.DisplayName)) { $orphan.ProductCode } else { $orphan.DisplayName } $script:SACFailures += [PSCustomObject]@{ Component = "Installer residue (product): $label"; Reason = "Registered in $($orphan.Hives -join ', ') with no Add/Remove Programs entry" } } foreach ($orphan in $Residue.OrphanedPatches) { $label = if ([string]::IsNullOrWhiteSpace($orphan.DisplayName)) { $orphan.PatchCode } else { $orphan.DisplayName } $script:SACFailures += [PSCustomObject]@{ Component = "Installer residue (patch): $label"; Reason = "Registered in $($orphan.Hives -join ', ') but linked to zero products" } } $StopWatch.Stop() $ElapsedTime = "{0:mm} min {0:ss} sec" -f $StopWatch.Elapsed Write-SACMsg "==========================================" "Info" if ($Halted) { Write-SACMsg " PURGE HALTED after $($ElapsedTime) - REBOOT REQUIRED" "Warning" } else { Write-SACMsg " PURGE COMPLETED in $($ElapsedTime)" "Success" } Write-SACMsg "==========================================" "Info" if ($script:SACRebootRequired) { Write-Host "`n[!] REBOOT REQUIRED - the purge stopped before completing." -ForegroundColor Yellow Write-Host " An uninstaller returned 3010 (ERROR_SUCCESS_REBOOT_REQUIRED). Windows Installer" -ForegroundColor Gray Write-Host " keeps the product registered until the reboot finishes the transaction, so the" -ForegroundColor Gray Write-Host " Add/Remove Programs entry was kept and no further removal was attempted:" -ForegroundColor Gray foreach ($blocker in $script:SACRebootBlockers) { Write-Host " - $blocker" -ForegroundColor Yellow } Write-Host " Reboot, then re-run the purge to finish and re-verify.`n" -ForegroundColor Gray if ((Test-SACInteractive -Silent $Silent) -and -not $IsPreview) { $RebootResp = Read-Host " Reboot now? (y/N)" if ($RebootResp.Trim().ToLower() -eq 'y' -and $PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Restart computer')) { Restart-Computer -Force } } } if ($script:SACFailures.Count -gt 0) { Write-Host "`n[!] CRITICAL COMPONENT FAILURES DETECTED:" -ForegroundColor Red foreach ($fail in $script:SACFailures) { Write-Host " - $($fail.Component)" -ForegroundColor Yellow Write-Host " Reason: $($fail.Reason)" -ForegroundColor DarkGray } Write-Host "`nPlease review the Debug Log for deeper diagnostics or resolve locks manually.`n" -ForegroundColor Red } else { Write-Host "`n[*] All operations completed successfully with no critical failures.`n" -ForegroundColor Green } # Prompt if there were locked files/folders and we are not running silently if ($script:SACLockedItemsList.Count -gt 0 -and -not $Silent -and -not $IsPreview) { Write-Host "`n[!] There were $($script:SACLockedItemsList.Count) files/directories locked or in-use." -ForegroundColor Yellow $resp = Read-Host " Would you like to schedule them for removal on the next restart? (y/N)" if ($resp.Trim().ToLower() -eq 'y') { Write-Host "`n Scheduling post-reboot cleanup..." -ForegroundColor Cyan $registered = Register-SACPostRebootCleanup -Paths $script:SACLockedItemsList.ToArray() if ($registered) { Write-Host " [OK] Post-reboot cleanup registered successfully. It will run silently on next logon." -ForegroundColor Green } else { Write-Host " [!] Failed to register post-reboot cleanup." -ForegroundColor Red } Start-Sleep -Seconds 2 } } # Persist outcome so the interactive menu can show a status badge on return $AttentionFile = Join-Path $LogDir "AttentionItems.txt" if ($script:SACFailures.Count -gt 0) { $content = @( "AUTODESK MASTER PURGE - ITEMS REQUIRING ATTENTION", "Timestamp: $(Get-Date)", "Log Directory: $LogDir", "----------------------------------------------------------", "" ) if ($script:SACRebootRequired) { $content += "[!] REBOOT REQUIRED - the purge halted before completing." $content += " Reboot the machine and re-run the purge before removing anything else." $content += "" } foreach ($fail in $script:SACFailures) { $content += "[!] $($fail.Component)" $content += " Reason: $($fail.Reason)" $content += "" } $content | Out-File -FilePath $AttentionFile -Encoding utf8 -WhatIf:$false } $script:SACLastRunStatus = [PSCustomObject]@{ Operation = 'Master Purge' Criticals = $script:SACFailures.Count Warnings = 0 Elapsed = $ElapsedTime LogDir = $LogDir AttentionItems = if ($script:SACFailures.Count -gt 0) { $AttentionFile } else { $null } RebootRequired = [bool]$script:SACRebootRequired RebootBlockers = @($script:SACRebootBlockers) InstallerResidue = $Residue.Total ResidueReport = $script:SACResidueReport } return $script:SACLastRunStatus } # --- Execution Block --- Clear-Host Write-SACMsg "==========================================" "Info" Write-SACMsg " AUTODESK MASTER PURGE INITIALIZED" "Info" Write-SACMsg " Transcript: $($TranscriptLog)" "Info" Write-SACMsg " Debug Log: $($DebugLog)" "Info" Write-SACMsg "==========================================" "Info" if (Test-SACInteractive -Silent $Silent) { $PendingReboot = Test-SACPendingReboot if ($PendingReboot) { Write-Host "[!] WARNING: A pending reboot is detected on this system." -ForegroundColor Yellow foreach ($reason in (Get-SACPendingRebootReason)) { Write-Host " - $reason" -ForegroundColor DarkYellow } $ProceedPurge = Read-Host "Would you like to proceed with the Master Purge anyway? (y/N)" if ($ProceedPurge.Trim().ToLower() -ne 'y') { $RebootPrep = Read-Host "Are you planning to reboot, and would you like to disable or remove any applicable scheduled tasks and services first? (y/N)" if ($RebootPrep.Trim().ToLower() -eq 'y') { Write-SACMsg "Disabling and removing Autodesk scheduled tasks and services before reboot..." "Warning" Stop-AndRemoveService -ServiceName "Autodesk" Stop-AndRemoveService -ServiceName "Adsk" Stop-AndRemoveService -ServiceName "ODIS" Stop-AndRemoveService -ServiceName "AdskAccessService" Stop-AndRemoveService -ServiceName "AGS" Stop-AndRemoveService -ServiceName "Genuine" Get-ScheduledTask -TaskPath "\Autodesk\*" -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue Get-ScheduledTask -TaskName "*Autodesk*" -ErrorAction SilentlyContinue | Disable-ScheduledTask -ErrorAction SilentlyContinue Write-SACMsg "Autodesk scheduled tasks and services disabled/removed. Please reboot the machine and re-run the tool." "Success" } else { Write-SACMsg "Execution aborted. No changes made." "Warning" } Stop-Transcript | Out-Null return } } Write-Host "`nWARNING: This will forcefully terminate and remove all Autodesk applications.`n" -ForegroundColor Yellow $Response = Read-Host "Type 'YES' to proceed" if ($Response -ne "YES") { Write-SACMsg "Execution aborted by user." "Warning" Stop-Transcript | Out-Null return } $ClearTempChoice = $ClearTemp if (-not $ClearTemp) { $TempResp = Read-Host "Would you like to clear all files in the Temp folders (user temp and Windows temp)? (y/N)" if ($TempResp.Trim().ToLower() -eq 'y') { $ClearTempChoice = $true } } } else { Write-SACMsg "Running in non-interactive/silent mode." "Info" if (Test-SACPendingReboot) { Write-SACMsg "WARNING: A pending system reboot has been detected: $((Get-SACPendingRebootReason) -join '; ')" "Warning" } $ClearTempChoice = $ClearTemp } foreach ($product in $RemoveVersions) { Invoke-UninstallAutodeskProduct -ProductName $product.Name -Versions $product.Versions if ($script:SACRebootRequired) { break } } # A 3010 means the MSI transaction is incomplete. Continuing to strip services, # registry hives and directories out from under it is what turns a pending reboot # into an unrepairable product, so the run ends here. if ($script:SACRebootRequired) { Write-SACMsg "Reboot barrier reached - skipping service removal, registry hive wipe and file system wipe." "Warning" $HaltedStatus = Complete-SACPurgeRun -Halted Stop-Transcript | Out-Null return $HaltedStatus } # --- Phase 2: Service and System Component Removal --- Write-SACMsg "All product uninstallers completed. Proceeding to service removal..." "Info" Stop-AndRemoveService -ServiceName "Autodesk" Stop-AndRemoveService -ServiceName "Adsk" Stop-AndRemoveService -ServiceName "ODIS" Stop-AndRemoveService -ServiceName "AdskAccessService" Stop-AndRemoveService -ServiceName "AGS" Stop-AndRemoveService -ServiceName "Genuine" Invoke-RemoveODISAndLicensing Invoke-RemoveSQLLocalDB Write-SACMsg "Purging Installer Cache..." "Info" $InstallerCache = Get-ItemProperty -Path "HKLM:\SOFTWARE\Classes\Installer\Products\*" -ErrorAction SilentlyContinue | Where-Object { $_.ProductName -Like "*Autodesk*" } foreach ($cache in $InstallerCache) { if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $cache.PSPath -Action 'Remove installer cache key')) { continue } try { Remove-Item $cache.PSPath -Recurse -Force -ErrorAction Stop } catch { Write-SACQuietLog "Failed to purge installer cache key $($cache.PSPath): $($_.Exception.Message)" } } Write-SACMsg "Wiping Registry Hive..." "Info" # -WhatIf:$false - mapping a drive is not a change, and the preview needs it to resolve HKU:\* New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS\ -ErrorAction SilentlyContinue -WhatIf:$false | Out-Null foreach ($location in $RegistryLocations) { # Resolve any wildcards in the path without using -Recurse $resolvedPaths = Get-Item $location -ErrorAction SilentlyContinue foreach ($regKey in $resolvedPaths) { $nativeRegPath = $regKey.Name if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $nativeRegPath -Action 'Delete registry tree')) { continue } try { # Use reg.exe because it avoids PowerShell's StackOverflowException on extremely deep/cyclic keys $regArgs = "delete `"$nativeRegPath`" /f" $regProc = Start-Process -FilePath "reg.exe" -ArgumentList $regArgs -Wait -NoNewWindow -PassThru if ($regProc.ExitCode -eq 0) { Write-SACMsg "Removed registry tree: $nativeRegPath" "Success" } else { Write-SACQuietLog "reg.exe failed to remove $nativeRegPath. Exit code: $($regProc.ExitCode)" } } catch { Write-SACQuietLog "Failed to execute reg delete for $($nativeRegPath): $($_.Exception.Message)" } } } Write-SACMsg "Wiping File System..." "Info" Start-Sleep -Seconds 3 foreach ($location in $DataLocations) { # Separate wildcard paths (need Get-Item expansion) from literal paths. # Get-Item returns nothing for a literal path whose root was partially deleted # by a previous uninstaller — even when subdirectories still exist on disk. # Test-Path -LiteralPath is immune to this and correctly detects those survivors. if ($location -match '\*') { $resolvedPaths = Get-Item -Path $location -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName } else { $resolvedPaths = if (Test-Path -LiteralPath $location) { @($location) } else { @() } } foreach ($fp in $resolvedPaths) { if (-not (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target $fp -Action 'Purge directory')) { continue } $purgeResult = Invoke-SACRobocopyPurge -TargetPath $fp if (-not $purgeResult.Success) { Write-SACQuietLog "Failed to fully remove directory $fp (files likely locked)." $failReason = "Files are locked/in-use." if ($purgeResult.LockedItems.Count -gt 0) { $failReason += " Locked Items: $($purgeResult.LockedItems -join ', ')" } $script:SACFailures += [PSCustomObject]@{ Component = "Directory Purge (Partial): $fp"; Reason = $failReason } if ($purgeResult.LockedItems.Count -gt 0) { Write-SACMsg " Queuing $($purgeResult.LockedItems.Count) locked item(s) for deletion on reboot." "Warning" Invoke-SACPendingDelete -Paths $purgeResult.LockedItems foreach ($item in $purgeResult.LockedItems) { if (-not $script:SACLockedItemsList.Contains($item)) { $script:SACLockedItemsList.Add($item) } } } } else { Write-SACMsg "Purged directory: $fp" "Success" } } } Invoke-SACShortcutPurge # Wipe specific Autodesk temp files and directories if (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target 'Autodesk temp files' -Action 'Delete') { Invoke-SACTempAutodeskCleanup } # Optionally clear the entire Temp folders (user and Windows Temp) if ($ClearTempChoice -and (Test-SACShouldProcess -Cmdlet $PSCmdlet -Target 'User and Windows temp folders' -Action 'Clear')) { Clear-SACTempFolders } $FinalStatus = Complete-SACPurgeRun Stop-Transcript | Out-Null return $FinalStatus } # SIG # Begin signature block # MII9OgYJKoZIhvcNAQcCoII9KzCCPScCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAYrt+KsFSpDaiH # 82ncQM9zLUNNZTqY7bzdiU8ggYYdgaCCIfwwggXMMIIDtKADAgECAhBUmNLR1FsZ # lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu # dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy # MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv # c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo # b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD # Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH # or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD # PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV # qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70 # ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1 # vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3 # W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK # xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg # trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj # O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7 # swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ # T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn # LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c # Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF # xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx # AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8 # FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY # lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2 # 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY # uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al # GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk # ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T # 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggarMIIEk6ADAgECAhMzAAYgdEwI # qMbC9o3UAAAABiB0MA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ # RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDMwHhcNMjYwOTA4MDM1NTU5WhcNMjYwOTEx # MDM1NTU5WjBtMQswCQYDVQQGEwJVUzELMAkGA1UECBMCZ2ExETAPBgNVBAcTCEJv # c3R3aWNrMR4wHAYDVQQKExVSaWNoYXJkIERhaWxlbiBHdW50ZXIxHjAcBgNVBAMT # FVJpY2hhcmQgRGFpbGVuIEd1bnRlcjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCC # AYoCggGBAMaVHw1xEKM/ypHgCoqzRLfs3ifk+jtX1alGru9yVQJl6XNgN3RQboVV # HGa4/Y4xvsfJKfcfYxr/2EuZM2dpsNmPVwAoLqRn9NrFSvM8EEIviwZjNZPFszwq # /nWUtCL1odXE6qpca2JXyYtWeGhE8XHP1LxLZ6NaAcBy1aCsliSSt45PjMqLWaw1 # 16YFWiuas9311NDYlxH5+Fz1Tqqo8SEeNioekzo0cJyDBkASuyQcf1LXy22LdNQQ # WCq6vw6bjjmS1tQZc06X6mMQYQnJdHVwqWSwzXPHtaMSXXApaiyY1SnpztOGNXF0 # Jd4u8TblLFpKmoPDCp3nmv6wHBtBwA60CVnGH0Ga3QTrqDBxUd8o7eEAhup2mOwp # U/epYC3fKOZhpq8FHnjBoxSKf6oa7LQfe53TwfdLmC5LRqROPp06FW87ifoWnPOp # I5HCNPdsokl9BoH2mBCEvywqrhUZxzZMGSvKlyGjvApE+bTNsF9N/NEzZXeQ1Eq1 # ZhybQzkyPQIDAQABo4IB1TCCAdEwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMC # B4AwPAYDVR0lBDUwMwYKKwYBBAGCN2EBAAYIKwYBBQUHAwMGGysGAQQBgjdhoOiV # eoK/0ttMg5+ekiyCi8TIKTAdBgNVHQ4EFgQU4KW1b5S1TD1sjhK66eR2C9UMLBcw # HwYDVR0jBBgwFoAUpEMMf3ZapYXnPo0oDwwXokVpcMYwZwYDVR0fBGAwXjBcoFqg # WIZWaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwQU9DJTIwQ0ElMjAwMy5jcmwwdAYIKwYB # BQUHAQEEaDBmMGQGCCsGAQUFBzAChlhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEFP # QyUyMENBJTIwMDMuY3J0MFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y # eS5odG0wDQYJKoZIhvcNAQEMBQADggIBABs65KrxSZxozLE987sSET+UmFgwaO77 # nelKKU/51CQqwtFhE8NVMc4RG3Jt2ExmVSDMxc9BJEXro0k1M5aI81qHSMU7gogF # C5Iug+/Y515Bst++G4nPiu+NsVbGlDrAPGho+NXDDg9xkCNOlga5qbl8LKmAQ/ar # p5L7BZY/qFhDFPvak0I89Y7wHEyQln8cXbysgOdKPbxbltNrDHb/JDsAU1H6Qy4P # wOcefY/z1/mPhIXMpFtMGCWtNfaSP1QfsPiYd5PdDFvJbChiVyxZA56hPBeNTRgF # y1Are1HEtvXXZePCIQSr0z6N6gdx8/b0DzRx9ABjhJzfnZaSO85fskIjWhl+/hcA # 1nhvhN2BZFX8UodOXgfu9DDJ8+1d22skEH0oyMj6WlFzGteArOEPH/s6v1diN5Wl # g3EaOvZ19eKLk92UO5dkWcw9bKiR5m/fZwo9XKTqR0bktovIGrKdp3FKTxemzq0P # a8V737OKHSingPM2zf1+1rIP+Z3jEieeCxiNkvxeadckSI9cC/sWcPKatof5tQ7f # x/kEOXbKmtSBy0lIkGTQMzd0rdg+ejHkICQBHQoNpLQXUDB8g1AQQbxp1t5y33VZ # Gkap7+f+72HnROJRTD8syZmM3O2ZP7UZgZ41gRVy3GMV0YmmAOMX+S7rZSw9wRLV # 3iXbj1569sk7MIIGqzCCBJOgAwIBAgITMwAGIHRMCKjGwvaN1AAAAAYgdDANBgkq # hkiG9w0BAQwFADBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgQU9D # IENBIDAzMB4XDTI2MDkwODAzNTU1OVoXDTI2MDkxMTAzNTU1OVowbTELMAkGA1UE # BhMCVVMxCzAJBgNVBAgTAmdhMREwDwYDVQQHEwhCb3N0d2ljazEeMBwGA1UEChMV # UmljaGFyZCBEYWlsZW4gR3VudGVyMR4wHAYDVQQDExVSaWNoYXJkIERhaWxlbiBH # dW50ZXIwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDGlR8NcRCjP8qR # 4AqKs0S37N4n5Po7V9WpRq7vclUCZelzYDd0UG6FVRxmuP2OMb7HySn3H2Ma/9hL # mTNnabDZj1cAKC6kZ/TaxUrzPBBCL4sGYzWTxbM8Kv51lLQi9aHVxOqqXGtiV8mL # VnhoRPFxz9S8S2ejWgHActWgrJYkkreOT4zKi1msNdemBVormrPd9dTQ2JcR+fhc # 9U6qqPEhHjYqHpM6NHCcgwZAErskHH9S18tti3TUEFgqur8Om445ktbUGXNOl+pj # EGEJyXR1cKlksM1zx7WjEl1wKWosmNUp6c7ThjVxdCXeLvE25SxaSpqDwwqd55r+ # sBwbQcAOtAlZxh9Bmt0E66gwcVHfKO3hAIbqdpjsKVP3qWAt3yjmYaavBR54waMU # in+qGuy0H3ud08H3S5guS0akTj6dOhVvO4n6FpzzqSORwjT3bKJJfQaB9pgQhL8s # Kq4VGcc2TBkrypcho7wKRPm0zbBfTfzRM2V3kNRKtWYcm0M5Mj0CAwEAAaOCAdUw # ggHRMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMDwGA1UdJQQ1MDMGCisG # AQQBgjdhAQAGCCsGAQUFBwMDBhsrBgEEAYI3YaDolXqCv9LbTIOfnpIsgovEyCkw # HQYDVR0OBBYEFOCltW+UtUw9bI4SuunkdgvVDCwXMB8GA1UdIwQYMBaAFKRDDH92 # WqWF5z6NKA8MF6JFaXDGMGcGA1UdHwRgMF4wXKBaoFiGVmh0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQl # MjBDUyUyMEFPQyUyMENBJTIwMDMuY3JsMHQGCCsGAQUFBwEBBGgwZjBkBggrBgEF # BQcwAoZYaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNy # b3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBBT0MlMjBDQSUyMDAzLmNydDBU # BgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMA0GCSqGSIb3DQEB # DAUAA4ICAQAbOuSq8UmcaMyxPfO7EhE/lJhYMGju+53pSilP+dQkKsLRYRPDVTHO # ERtybdhMZlUgzMXPQSRF66NJNTOWiPNah0jFO4KIBQuSLoPv2OdeQbLfvhuJz4rv # jbFWxpQ6wDxoaPjVww4PcZAjTpYGuam5fCypgEP2q6eS+wWWP6hYQxT72pNCPPWO # 8BxMkJZ/HF28rIDnSj28W5bTawx2/yQ7AFNR+kMuD8DnHn2P89f5j4SFzKRbTBgl # rTX2kj9UH7D4mHeT3QxbyWwoYlcsWQOeoTwXjU0YBctQK3tRxLb112XjwiEEq9M+ # jeoHcfP29A80cfQAY4Sc352WkjvOX7JCI1oZfv4XANZ4b4TdgWRV/FKHTl4H7vQw # yfPtXdtrJBB9KMjI+lpRcxrXgKzhDx/7Or9XYjeVpYNxGjr2dfXii5PdlDuXZFnM # PWyokeZv32cKPVyk6kdG5LaLyBqynadxSk8Xps6tD2vFe9+zih0op4DzNs39ftay # D/md4xInngsYjZL8XmnXJEiPXAv7FnDymraH+bUO38f5BDl2yprUgctJSJBk0DM3 # dK3YPnox5CAkAR0KDaS0F1AwfINQEEG8adbect91WRpGqe/n/u9h50TiUUw/LMmZ # jNztmT+1GYGeNYEVctxjFdGJpgDjF/ku62UsPcES1d4l249eevbJOzCCBygwggUQ # oAMCAQICEzMAAAAYDeuRVamKAJgAAAAAABgwDQYJKoZIhvcNAQEMBQAwYzELMAkG # A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UE # AxMrTWljcm9zb2Z0IElEIFZlcmlmaWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTAe # Fw0yNjAzMjYxODExMzJaFw0zMTAzMjYxODExMzJaMFoxCzAJBgNVBAYTAlVTMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29m # dCBJRCBWZXJpZmllZCBDUyBBT0MgQ0EgMDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC # DwAwggIKAoICAQDIgNpgNFaiif2VWeWP5I6PnFXxJ/lB37fJR55GCvR7GLZBMkBi # jbiKVwgpBI3xM5nf484znH/qncJ+OCq6y3jgnQW+R8Zd7U+7LjlrmcskalzSQ0gh # MxEpnBW8/HHs2V8ZJzQk6HP+SDsbvsL7LdlH/eO2l4mknhDBwr0Z/Q966TvEth5b # 8kCxj1vqiV4YNthLGRqZR9u2fK/yBMWu83p6O4uo2Edg++gEew5IL7vnnnKFqmSh # /R9vPJy3WF1YcZewAUx8sXZNUnx3ZhVg59l2LpitPiwzE6FMqIsqaEvVe3MzuFd2 # a/uWDZH6VbDyUiRK78mIg1DQYA9zDEyyBFcNI+nxVSzglvL6u7PRuNqgcV3sf6EL # xw89ysQM/Z4R1hRFWXRpyOWKKAKtfBHTk0UnNiPcxmLMMYs8jeUjOidfVPjTIry/ # UVwnwxdlkK85cZfBEMYZ/DBNOwdomP459Y1n8izKkbhsa+p4lw+cQVxATBFx9ggR # 79HhryT7HDmpPLvkJvBZ4wW4CW32UT2SMyDe28nIOU3m+hfHlVeKcLBQcym5VoRD # jIcCVI7uqgGW2PNME0cfei8zCwCy6HCsssJWFS7eg/YbFhnATJcyWfMrkNuAbMfM # N8Npg8crS6jVVowyD0GG5zdgi+uQVcSK/638mA1xEYK3pnIoQgO09uuDBwIDAQAB # o4IB3DCCAdgwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1Ud # DgQWBBSkQwx/dlqlhec+jSgPDBeiRWlwxjBUBgNVHSAETTBLMEkGBFUdIAAwQTA/ # BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2Nz # L1JlcG9zaXRvcnkuaHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1Ud # EwEB/wQIMAYBAf8CAQAwHwYDVR0jBBgwFoAU2UEpsA8PY2zvadf1zSmepEhqMOYw # cAYDVR0fBGkwZzBloGOgYYZfaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9jcmwvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENvZGUlMjBTaWduaW5n # JTIwUENBJTIwMjAyMS5jcmwwfQYIKwYBBQUHAQEEcTBvMG0GCCsGAQUFBzAChmFo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUy # MElEJTIwVmVyaWZpZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3J0 # MA0GCSqGSIb3DQEBDAUAA4ICAQBxxyBW+X6mhdRiSwD9PMMWcGUAnx5/QUwnNvZd # FGEX+4DRDIr9WCh4C87wHtw+lg1D3uzK10DstPX0LFLBFAC3vWMYX4ImXwoLhoR0 # xlN8mUdorJ3bgnpCJWuI1531Z1rCwPuUrSkBxfOIGDk3p2ECb3Ho/xHi5PRSR/OU # rWuQHwXiaXMTuXu3IRLezwVkZpFmNwYRD57R9Nx2F/yM7tzOY0Hh0hGCaYEK38/6 # FrS0SXadXWyDUCfn5XOGACRjUCnHx+JQUG0f4SHD+iblpAI0gl+ZHnVmdXXxHTZe # Ta0CYCIhFxKP2922s0g6zLmeiV13LWUmtt/UF7TrWXpMi2/0UNniaDoH7rnPGRV5 # xVX8uXy4sZii4aswzqPM7Y7+mzcranqZ8EjZk5gjLhQ3A2sZaprlOu8CaRmyfcIi # VH7zVfgAvm81MWXFziAf7my7QOvnyEFPGddq8MSfPtfRyw/Uq3uH6KpoaJNIfPYH # 6fceZSi53Rat1A9grExq3ROjhhSpTcchuBItAMNVPxoKNbUm+iR/X3XkL+9WQgin # jyHe+hXLclY8vAGXFD1p40PqMIpAYsmEJBFKW9df4//1N5oQDr/FY9IBJl/oSS97 # 9i5rtT7NZz9KvYraCPRBGs0QCy+sWvgQa0coM70QJVLeVwmSxUO/0od0w9Qry7bS # LrxGoDCCB54wggWGoAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJKoZIhvcN # AQEMBQAwdzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjFIMEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIwMDUyMFoX # DTM2MDQwMTIwMTUyMFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlmaWVkIENv # ZGUgU2lnbmluZyBQQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBALLwwK8ZiCji3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3qrZilYKJ # NqztA9OQacr1AwoNcHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrweLoQ71lz # CHkD7A4As1Bs076Iu+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9rGQ//h89 # 0Q8JdjLLw0nV+ayQ2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8sZU6ZE7iR # rRZywRmrKDp5+TcmJX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfWo5tTgkoS # MoayqoDpHOLJs+qG8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC69oKpRQXY # 9bGH6jn9NEJv9weFxhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjkVd6CGUAf # 0K+Jw+GE/5VpIVbcNr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHgYYjHs1Nb # Oc6brH+eKpWLtr+bGecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu0t+gCwLd # N0A6cFtSRtR8bvxVFyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeLzdpt32cd # YKp+dhr2ogc+qM6K4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGjggI1MIIC # MTAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNlB # KbAPD2Ns72nX9c0pnqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUF # BwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3Np # dG9yeS5odG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUw # AwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0w # ezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWlj # cm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlm # aWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEEgbYwgbMw # gYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Nl # cnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUy # MENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYBBQUHMAGG # IWh0dHA6Ly9vbmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG9w0BAQwF # AAOCAgEAfyUqnv7Uq+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdPQ4H55v7V # OInnmezQEeW4LqK0wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVIE1CRFfNF # 4XKO8XYEa3oW8oVH+KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T8FG7/VEU # RIVWArbAzPt9ThVN3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4e0ejRA7m # B2IoX5hNh3UEauY0byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaBjhWmirYy # ANb0RJSLWjinMLXNOAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/tuLjAsKP6 # SzYZ+1Ry358ZTFcx0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQwK8VtsBde # kBmdTbQVoCgPCqr+PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX3mccFgx6 # UsQeRSdVVVNSyALQe6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQLUz6ZTl65 # yPUiOh3k7Yww94lDf+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i3IpaDcR+ # iuGjH2TdaC1ZOmBXiCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxghqUMIIakAIB # ATBxMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBBT0MgQ0EgMDMC # EzMABiB0TAioxsL2jdQAAAAGIHQwDQYJYIZIAWUDBAIBBQCgXjAQBgorBgEEAYI3 # AgEMMQIwADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAvBgkqhkiG9w0BCQQx # IgQgJtwlfLDba3pZKIz5rfztWu89cyG43U1wM39jpOP/2yEwDQYJKoZIhvcNAQEB # BQAEggGAdnBUSA8MT8KFIwmJHhnSnF7Eq1YILHcvZtRj8o+ubEmYiZos3qWhoEO/ # MtEO6MCS4OdT8dTmYMNSxYjTJGg22TjCt9Ecmiyj+MZIJ5qy8/pTH4qYJG9DRmml # YRZhoE7iFijPJ25QIF/3Z9yyy5UqX66VwOJALfOOFQvl+I1XxhpZu/sDO0Yd1aoC # cJ3k85efCRENo/GzNb/hpw3WGArgf1mXd9DIRYxc5/DAZO4Ks/HXLhEWbdSBLPXG # SdMpLpcKxKZouLEyLg/MvegkAoWgDdGrIWbv22MIzPAkzxrroj68XhnCBCIQr3JH # Mml+u5i+57w4+542LGvgAqeiGN7lj3mbUsxhBZ8zzzX8JeQxAwsDw4agKQ83M3lX # 86MhRay05CfndpB6JJsV4aqkOASzDREVNudx3etVMsyiSqsei39WFi4LzW+TFvnS # vxk6JkOyxi94nmjd28y01FRCPIMhIcAxnGm9tZeu6V3P/JenN2CVtA5DxsNlNFaV # h+JPa2AkoYIYFDCCGBAGCisGAQQBgjcDAwExghgAMIIX/AYJKoZIhvcNAQcCoIIX # 7TCCF+kCAQMxDzANBglghkgBZQMEAgEFADCCAWIGCyqGSIb3DQEJEAEEoIIBUQSC # AU0wggFJAgEBBgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIJVSQnc10mcs # tK29PXUxo+KS1EQe3unW+d5Rw5PRzqZTAgZqhI09JbEYEzIwMjYwOTA5MTQzODM0 # LjgxMlowBIACAfSggeGkgd4wgdsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMx # JzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo3ODAwLTA1RTAtRDk0NzE1MDMGA1UE # AxMsTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmg # gg8hMIIHgjCCBWqgAwIBAgITMwAAAAXlzw//Zi7JhwAAAAAABTANBgkqhkiG9w0B # AQwFADB3MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMUgwRgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJv # b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAxMTE5MjAzMjMxWhcN # MzUxMTE5MjA0MjMxWjBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1l # c3RhbXBpbmcgQ0EgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB # AJ5851Jj/eDFnwV9Y7UGIqMcHtfnlzPREwW9ZUZHd5HBXXBvf7KrQ5cMSqFSHGqg # 2/qJhYqOQxwuEQXG8kB41wsDJP5d0zmLYKAY8Zxv3lYkuLDsfMuIEqvGYOPURAH+ # Ybl4SJEESnt0MbPEoKdNihwM5xGv0rGofJ1qOYSTNcc55EbBT7uq3wx3mXhtVmtc # CEr5ZKTkKKE1CxZvNPWdGWJUPC6e4uRfWHIhZcgCsJ+sozf5EeH5KrlFnxpjKKTa # vwfFP6XaGZGWUG8TZaiTogRoAlqcevbiqioUz1Yt4FRK53P6ovnUfANjIgM9JDdJ # 4e0qiDRm5sOTiEQtBLGd9Vhd1MadxoGcHrRCsS5rO9yhv2fjJHrmlQ0EIXmp4DhD # BieKUGR+eZ4CNE3ctW4uvSDQVeSp9h1SaPV8UWEfyTxgGjOsRpeexIveR1MPTVf7 # gt8hY64XNPO6iyUGsEgt8c2PxF87E+CO7A28TpjNq5eLiiunhKbq0XbjkNoU5Jht # YUrlmAbpxRjb9tSreDdtACpm3rkpxp7AQndnI0Shu/fk1/rE3oWsDqMX3jjv40e8 # KN5YsJBnczyWB4JyeeFMW3JBfdeAKhzohFe8U5w9WuvcP1E8cIxLoKSDzCCBOu0h # WdjzKNu8Y5SwB1lt5dQhABYyzR3dxEO/T1K/BVF3rV69AgMBAAGjggIbMIICFzAO # BgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGtpKDo1 # L0hjQM972K9J6T7ZPdshMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y # eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA # YgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcn # z1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRp # b24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCB # lAYIKwYBBQUHAQEEgYcwgYQwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVy # aWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAy # MC5jcnQwDQYJKoZIhvcNAQEMBQADggIBAF+Idsd+bbVaFXXnTHho+k7h2ESZJRWl # uLE0Oa/pO+4ge/XEizXvhs0Y7+KVYyb4nHlugBesnFqBGEdC2IWmtKMyS1OWIviw # pnK3aL5JedwzbeBF7POyg6IGG/XhhJ3UqWeWTO+Czb1c2NP5zyEh89F72u9UIw+I # fvM9lzDmc2O2END7MPnrcjWdQnrLn1Ntday7JSyrDvBdmgbNnCKNZPmhzoa8PccO # iQljjTW6GePe5sGFuRHzdFt8y+bN2neF7Zu8hTO1I64XNGqst8S+w+RUdie8fXC1 # jKu3m9KGIqF4aldrYBamyh3g4nJPj/LR2CBaLyD+2BuGZCVmoNR/dSpRCxlot0i7 # 9dKOChmoONqbMI8m04uLaEHAv4qwKHQ1vBzbV/nG89LDKbRSSvijmwJwxRxLLpMQ # /u4xXxFfR4f/gksSkbJp7oqLwliDm/h+w0aJ/U5ccnYhYb7vPKNMN+SZDWycU5OD # IRfyoGl59BsXR/HpRGtiJquOYGmvA/pk5vC1lcnbeMrcWD/26ozePQ/TWfNXKBOm # kFpvPE8CH+EeGGWzqTCjdAsno2jzTeNSxlx3glDGJgcdz5D/AAxw9Sdgq/+rY7jj # gs7X6fqPTXPmaCAJKVHAP19oEjJIBwD1LyHbaEgBxFCogYSOiUIr0Xqcr1nJfiWG # 2GwYe6ZoAF1bMIIHlzCCBX+gAwIBAgITMwAAAFck05XgounJMQAAAAAAVzANBgkq # hkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3Rh # bXBpbmcgQ0EgMjAyMDAeFw0yNTEwMjMyMDQ2NTNaFw0yNjEwMjIyMDQ2NTNaMIHb # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046NzgwMC0wNUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMg # UlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEAsWylCpMIfbizJLY1kPXO2cmX2HRWvRbAmeKSZ5ex7/jCymdV # 7Eap+Ic2iqRtWDkKKe5gL6JV80wtn5C2qHJLPxUYFKNG3UkHkAI21MoCN+YWnhT8 # K/YuPib6+6970jdbeFKIiZMWwd5hnpX9J3jeteuEdXbp/DfFBK15JuD3JOzWuF2s # uQCPgqYjQPk/gpq+3KCKtXJRbXSCSJ9YtITU2IHwmfdE7l2PfZ154w041po+fDeT # j0gJOzcV/Jv56Q0M+w19jAKo/I5PEzrLV1IPQnmP4or1X4RbJXk8ONXyOOfXOxK2 # VLpNxgklK1yAezbFP2uzqihaXkW1h9GQLGENKESnezwgdRaLNNaYtm8AT/pZHYJ3 # 5mZVqkZdMIckpQHJk/F1fSLyDKeKtH4TC4cc3ESKUMgItq07ZZm74JCsfhmrQ1ij # VNDi1Sln+QBamgC7WviZbkQnceQRq9DY+6hANwOrasAZUiVr2kPuj1jHDOXzUG4O # 9QTK70P/oXSqZAN1oTv3UfF8JTGmAxg+l1ZPOz50MY96HBDw/3bI/wBGNvLk6fLV # nrxGN5B5unF/lYvjjWbIUdyBPVQnPOKXu08SRHbY19M1HoWX6PNZv+vzSeqVeWWH # KdKjC3GjVjbbGpi+JLbiyaKRSwEqo49tJLvu69cQ7dWsbksai4TURnVj2mMCAwEA # AaOCAcswggHHMB0GA1UdDgQWBBSOg8leLTUOAglIZ+bjXpiD7RKSpzAfBgNVHSME # GDAWgBRraSg6NS9IY0DPe9ivSek+2T3bITBsBgNVHR8EZTBjMGGgX6BdhltodHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBQdWJs # aWMlMjBSU0ElMjBUaW1lc3RhbXBpbmclMjBDQSUyMDIwMjAuY3JsMHkGCCsGAQUF # BwEBBG0wazBpBggrBgEFBQcwAoZdaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBQdWJsaWMlMjBSU0ElMjBUaW1lc3RhbXBp # bmclMjBDQSUyMDIwMjAuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYI # KwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeAMGYGA1UdIARfMF0wUQYMKwYBBAGCN0yD # fQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lv # cHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTAIBgZngQwBBAIwDQYJKoZIhvcNAQEMBQAD # ggIBAHJ1wHY86Zk5SUBDPY25d/u9YJVaaNa71uxjX4cyO/XJ4uPENCSOwkRTnNog # PLxTD0Fg3z4TFf/2T/0IFSxdtWVtTjhzrn+WLInzeRawUhTCFVrPBJKEWVshm+Ig # 7/nB7JbJN88+ltImBbL5kT1StBLfG6UksAcDbNSQww90CUXhGueBxlnSvjkAX1oh # iN16y1bB2s0rvQx8Csepl2CuBefTfDrMGzW/tzNx5YaK2D8OWweqTWZcGlJO4YjZ # NI83cTrQghfHl/8AXOHj8cWL3wEFltQQs2xeRYAb3Kdnl7oIWKKXWaBYJY5P3QPs # iC+DTMp7ejdYKTrb396f3gr+wL/Ms5/Z3vIWZPJJv18qNw40fUNveRnwzMQnx8dM # 2bGuXXQZ5y7P8aXT4HJMo349qZtn4XQwiUE/DDp++MUL0kgjvd/Deo7Xr371PFPP # Yb4TboZhjV1x9+wCHDoOpNCBt+VuXU78ytJdKzQ1Jv2cEP1F9H9/wSLsMDUvWME7 # u9mGElOPDZPMVr8AuBEuLdbTSEdaLwsZBplzxLBcgxhZ/Cs30yBhuE3QhqT1YDZ2 # pa56RexPA2SasPcToT6gJgJ6E06BmZ2zQTNvWOjs5XQqHbYuXcoeDcwe2UaC7EDO # GD8GmLE9LiqtQsuQCM7v7I2xR+sPZT2Ax/85HjIkM+3MzTK1MYIHRjCCB0ICAQEw # eDBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0Eg # MjAyMAITMwAAAFck05XgounJMQAAAAAAVzANBglghkgBZQMEAgEFAKCCBJ8wEQYL # KoZIhvcNAQkQAg8xAgUAMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkq # hkiG9w0BCQUxDxcNMjYwOTA5MTQzODM0WjAvBgkqhkiG9w0BCQQxIgQgLZmDRvqM # jNzmUrpn1XUXWd48Tm9LLzAOaxOOtHc/e1gwgbkGCyqGSIb3DQEJEAIvMYGpMIGm # MIGjMIGgBCD1PJ9ktQVuTGWIbKLO4f1VUOlUU29ARCEpDZmFTHjbUjB8MGWkYzBh # MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIw # MAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAy # MAITMwAAAFck05XgounJMQAAAAAAVzCCA2EGCyqGSIb3DQEJEAISMYIDUDCCA0yh # ggNIMIIDRDCCAiwCAQEwggEJoYHhpIHeMIHbMQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVy # YXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046NzgwMC0wNUUwLUQ5NDcx # NTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0 # aG9yaXR5oiMKAQEwBwYFKw4DAhoDFQD9LzE5nEJRAUE2Ss3xaKKPXHnLw6BnMGWk # YzBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0Eg # MjAyMDANBgkqhkiG9w0BAQsFAAIFAO5LY9MwIhgPMjAyNjA5MDkwNDQ5MjNaGA8y # MDI2MDkxMDA0NDkyM1owdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA7ktj0wIBADAK # AgEAAgIRAwIB/zAHAgEAAgITITAKAgUA7ky1UwIBADA2BgorBgEEAYRZCgQCMSgw # JjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3 # DQEBCwUAA4IBAQAcGzZo21MhC18092oBxB7xFxebaCJkodJTLWJDBFOBfdqFGPSC # xqQeFDxSiZFMlK/PqTuQ7OeW2j5EJY2hSJWjdccLHiQx9hzz81j3mMM83Yd+P74f # C2M6DJas0kVloWW8WzK5fqecpuXLLlQxXaJFteHtVPXIH2VySqggLuygkjK+dQHM # xgSkpXeesWaGwEyrahuiN3cFJ/TP1Mu2JYjS7FlAIIE6QnlaP9/UNqVM2jBPyd89 # m47cSQ88DO+iPM5VtvQiQ0Hq7D65/uA95L1Kpvtd2B26CPprMkhC32Dnp6rEz647 # Ln4A9S1DmoR0MWBWeys1ULRXeM8r/yq2+/YdMA0GCSqGSIb3DQEBAQUABIICAIns # jW9xOoUkYDdi9w5AyhpNRol0g/mt9hGxxrpYZaOZCuGp/xCQ/Ws2GtUDF3Q68eoq # mprBiQbUspAUpaEm5DUQkTtX2IXXCC94ZKDgUpFXxyAe0a+8en1g2v70iJ4rVI6p # 1xxOc7jbqagIRWvvJfx24EKbwhwxXlJQhrb3nM5bFVwAR0As3vOnzZ8ypPf/M3un # GTl9wLaKXsj2oMQc43IepA5rSf6p7eFuc5yYOVDnJXhjDN7pff4BIy9keS/hCFfG # NSUDNmHaQBMhQ3t9woeXbtmL8PDQGgHhOkNh0MAwUEawuqrZ61T5yw6uR1bCIkSI # D2F9fY9aDFw6lQS64JOY/rSQ5D/fOI6nLC8XOefV+NwFMusfDDl+RxfUqg5Si0r3 # FPOBvTyVJDoTRruVgGB3grpoGcQ5Y5+VNWByyXeObQH+D9vPDLFJ81Lv4c3vT0IK # gIm/AuOr7FnfifFqgoOgg5Ci1DglEp+4C8UwsxchGMgL8NJVlIfYEV6wapuZIKj2 # 7WXBT0BNqHK75w6HaAyHJuh4rR1Xc8BiSBLVOAGHYPxhKmzblKl72pvfiSAEqIm9 # EjdckjGsBuiCN9qVlDgK80ppqCHk2WVkokmgOl1MTrb4LmMCWiw7mSHV+tkACvFb # T50dAm7e2wyLZhm6a680PMItnXPGNIf7KfpOoJdE # SIG # End signature block |