Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(#310) Uninstall-ChocolateyPath #1019

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Copyright 2011 - Present RealDimensions Software, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Uninstall-ChocolateyPath {
<#
.SYNOPSIS
**NOTE:** Administrative Access Required when `-PathType 'Machine'.`

This puts a directory to the PATH environment variable of the
requested scope (Machine or User).

.DESCRIPTION
Removes path from target path scope. Removes multiple occurances (if they exist)
and all occurances with or without a trailing slash.

.NOTES
This command will assert UAC/Admin privileges on the machine if
`-PathType 'Machine'`.

This is used when the application/tool is not being linked by Chocolatey
(not in the lib folder).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will also need to update this to indicate first supported version of Chocolatey this is in.


.INPUTS
None

.OUTPUTS
None

.PARAMETER PathToUninstall
The full path to a location to remove from the PATH.

.PARAMETER PathType
Which PATH to remove it from. If specifying `Machine`, this requires admin
privileges to run correctly.

.PARAMETER IgnoredArguments
Allows splatting with arguments that do not apply. Do not use directly.

.EXAMPLE
Uninstall-ChocolateyPath -PathToUninstall "$($env:SystemDrive)\tools\gittfs"

.EXAMPLE
Uninstall-ChocolateyPath "$($env:SystemDrive)\Program Files\MySQL\MySQL Server 5.5\bin" -PathType 'Machine'

.LINK
Install-ChocolateyPath

.LINK
Install-ChocolateyEnvironmentVariable

.LINK
Get-EnvironmentVariable

.LINK
Set-EnvironmentVariable

.LINK
Get-ToolsLocation
#>
param(
[parameter(Mandatory=$true, Position=0)][alias("Path")][string] $PathToUninstall,
[parameter(Mandatory=$false, Position=1)][ValidateSet('User','Machine','All')][alias("Scope")][String] $pathType = 'User',
[parameter(Mandatory=$false)][switch] $RecursiveCall,
[parameter(ValueFromRemainingArguments = $true)][Object[]] $ignoredArguments
)
Write-Debug "Running 'Uninstall-ChocolateyPath' with PathToUninstall:`'$PathToUninstall`'";
If (!$RecursiveCall -AND ($pathType -ine 'All')) {Write-Host "Only evaluating and updating path scope `"$pathType`", path will not be assessed nor removed for other scope, so path may exist in other scope as well."}
$originalPathToUninstall = $PathToUninstall
#First half on handling trailing slash properly - remove it from requested path:
$PathToUninstall = $PathToUninstall.trimend('\')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is trimend and is it available in PowerShell v2+ (and thus .NET Framework 2.0+)?

#array drops blanks (one of which is always created by final semi-colon)
$actualPathArrayUser = (Get-EnvironmentVariable -Name 'Path' -Scope 'user' -PreserveVariables).split(';',[System.StringSplitOptions]::RemoveEmptyEntries)
$actualPathArrayMachine = (Get-EnvironmentVariable -Name 'Path' -Scope 'machine' -PreserveVariables).split(';',[System.StringSplitOptions]::RemoveEmptyEntries)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're going to have to do something a little smarter since apparently semicolons can be escaped using quotes. Too bad there isn't a Windows API for this.

Reassembling the path will have to follow the same procedure in reverse.

Copy link

@jnm2 jnm2 Jan 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed using Windows' environment variable list editor: a tl;dr entry causes ;"tl;dr"; to be added to the path. (Luckily a path can't contain ".)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the path needs to be recognized and removed even if it is quoted in the PATH variable. I assume the input path would never be quoted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So luckily PATH doesn't have " and it doesn't have escaped ; - if you make it have ", it doesn't like you anymore IIRC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I understanding that a semi-colon is allowed in a folder name???!!!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just died a little inside. 👎


$PathFoundInMachine = $PathFoundInUser = $False
If (($actualpathArrayMachine -icontains "$($PathToUninstall.ToLower())") -OR ($actualpathArrayMachine -icontains "$(($PathToUninstall + '\').ToLower())"))
{
$PathFoundInMachine = $True
}

If (($actualpathArrayUser -icontains "$($PathToUninstall.ToLower())") -OR ($actualpathArrayUser -icontains "$(($PathToUninstall + '\').ToLower())"))
{
$PathFoundInUser = $True
}

#Process machine first to minimize suppression of messaging when recursion is necessary to process machine path
If ($PathFoundInMachine)
{
If (!$RecursiveCall) {Write-Host "Target path `"$PathToUninstall`" exists in Machine scope..."}
If ($pathType -ieq 'User' -AND ($pathType -ine 'All'))
{
If (!$RecursiveCall) {Write-Host "`"$PathToUninstall`" will only be removed from Machine scope per your request. Use -PathType 'User' to remove only from Machine scope or -PathType 'All' to remove from all scopes."}
}


If (($pathType -ieq 'Machine') -OR ($pathType -ieq 'All'))
{
If (!$RecursiveCall) {Write-Host "PATH environment variable for scope `"Machine`" contains `"$PathToUninstall`". Removing..."}
$actualpathArray = $actualPathArrayMachine
[string[]]$Newpatharray = $null
foreach ($path in $actualpathArray)
{
#second half of handling trailing slash properly - compare to both options in target path
If (($path -ine "$PathToUninstall") -AND ($path -ine "$($PathToUninstall)\"))
{
[string[]]$Newpatharray += "$path"
}
}
$actualPath = ($Newpatharray -join(';')) + ';'

if (Test-ProcessAdminRights)
{
Set-EnvironmentVariable -Name 'Path' -Value $actualPath -Scope 'Machine'
}
ElseIf (!$RecursiveCall)
{
$psArgs = "Uninstall-ChocolateyPath -PathToUninstall `'$originalPathToUninstall`' -pathType `'Machine`' -RecursiveCall"
Start-ChocolateyProcessAsAdmin "$psArgs"
If ($RecursiveCall) {Return}
}
Else
{
Throw "Did not gain admin rights on the recursive call, exiting to avoid going into recursive loop."
}
}
}

If ($PathFoundInUser)
{
Write-Host "Target path `"$PathToUninstall`" exists in User scope..."
If ($pathType -ine 'Machine' -AND ($pathType -ine 'All'))
{
Write-Host "`"$PathToUninstall`" will only be removed from User scope per your request. Use -PathType 'Machine' to remove only from Machine scope or -PathType 'All' to remove from all scopes."
}

If (($pathType -ieq 'User') -OR ($pathType -ieq 'All'))
{
Write-Host "PATH environment variable for scope `"User`" contains `"$PathToUninstall`". Removal Removing..."
$actualpathArray = $actualPathArrayUser
[string[]]$Newpatharray = $null
foreach ($path in $actualpathArray)
{
#second half of handling trailing slash properly - compare to both options in target path
If (($path -ine "$PathToUninstall") -AND ($path -ine "$($PathToUninstall)\"))
{
[string[]]$Newpatharray += "$path"
}
}
$actualPath = ($Newpatharray -join(';')) + ';'
Set-EnvironmentVariable -Name 'Path' -Value $actualPath -Scope 'User'
}
}

If ($PathFoundInUser -OR $PathFoundInMachine)
{
Write-Host "Updating environment for current process"
Update-SessionEnvironment
}
Else
{
Write-Host "`"$PathToUninstall`" was not found in requested scope `"$PathType`". Nothing to do..."
}
}