diff --git a/src/chocolatey-core.extension/CHANGELOG.md b/src/chocolatey-core.extension/CHANGELOG.md
new file mode 100644
index 0000000..9839389
--- /dev/null
+++ b/src/chocolatey-core.extension/CHANGELOG.md
@@ -0,0 +1,71 @@
+# CHANGELOG
+
+## 1.3.5
+
+- Bugfix `Remove-Process`: Fixed Powershell v2 compatibility issue
+
+## 1.3.4
+
+- Added `Remove-Process` function to ensure that process is stopped in reliable way
+
+## 1.3.3
+
+- Bugfix `Get-AppInstallLocation`: fix path is directory
+
+## 1.3.2
+
+- Bugfix `Get-AppInstallLocation`: now checks if path is directory
+
+## 1.3.1
+- Bugfix in `Get-AppInstallLocation`: Removed extra `$location` parameter from Split-Path when parsing the registry UninstallString.
+
+## 1.3.0
+
+- `Get-EffectiveProxy`: Get the current proxy using several methods
+
+## 1.2.0
+
+- Use `$IgnoredArguments` in all functions to allow for future expansion and splatting ([#621](https://github.com/chocolatey/chocolatey-coreteampackages/issues/621))
+- Bugfix in `Get-PackageParameters` parsing of paths containing symbol chars.
+
+## 1.1.0
+- `Get-AvailableDriveLetter`: Get the next unused drive letter
+
+## 1.0.7
+- Bugfix in `Get-PackageParameters`: flags can now have numbers in their names, whereas before, everything past the number would be truncated and the flag would turn into a boolean.
+
+## 1.0.6
+- Bugfix in `Get-AppInstallLocation`: Powershell 2 can not replace on null value.
+
+## 1.0.5
+
+- Bugfix in `Get-UninstallRegistryKey`: Powershell 2 compatibility.
+- Slightly improved documentation of `Get-UninstallRegistryKey`.
+
+## 1.0.4
+
+- Bugfix in `Get-PackageParameters`: Powershell 2 bug workaround ([#465](https://github.com/chocolatey/chocolatey-coreteampackages/issues/465)).
+
+## 1.0.3
+
+- Bugfix in `Get-PackageParameters`: error when parsing of path.
+
+## 1.0.2
+
+- Bugfix in `Get-PackageParameters`: PowerShell 2 compatibility.
+
+## 1.0.1
+
+- Bugfix in `Get-PackageParameters`: unaliased `sls` to work on PowerShell 2.
+
+## 1.0
+
+- Merged `mm-choco.extension`.
+- Merged `chocolatey-uninstall.extension`.
+- Added `Get-PackageCacheLocation`
+- Added `CHANGELOG.md` and `README.md`.
+- Refactoring and more documentation.
+
+## 0.1.3
+
+- `Get-WebContent`: Download file with choco internals.
diff --git a/src/chocolatey-core.extension/README.md b/src/chocolatey-core.extension/README.md
new file mode 100644
index 0000000..500e701
--- /dev/null
+++ b/src/chocolatey-core.extension/README.md
@@ -0,0 +1,50 @@
+# chocolatey-core.extension
+
+This is the Powershell module that extends Chocolatey with new functions.
+
+## Installation
+
+Install via chocolatey: `choco install chocolatey-core.extension`.
+
+The module is usually automatically installed as a dependency.
+
+## Usage
+
+To create a package that uses an extension function add the following to the `nuspec` specification:
+
+
+
+
+
+**NOTE**: Make sure you use adequate _minimum_ version.
+
+To test the functions you can import the module directly or via the `chocolateyInstaller.psm1` module:
+
+ PS> import-module $Env:ChocolateyInstall\helpers\chocolateyInstaller.psm1
+ PS> import-module $Env:ChocolateyInstall\extensions\chocolatey-core\*.psm1
+
+You can now test any of the functions:
+
+ PS> Get-AppInstallLocation choco -Verbose
+
+ VERBOSE: Trying local and machine (x32 & x64) Uninstall keys
+ VERBOSE: Trying Program Files with 2 levels depth
+ VERBOSE: Trying PATH
+ C:\ProgramData\chocolatey\bin
+
+Keep in mind that function may work only in the context of the `chocolateyInstaller.ps1`.
+
+To get the list of functions, load the module directly and invoke the following command:
+
+ Get-Command -Module chocolatey-core
+
+To get the help for the specific function use `man`:
+
+ man Get-UninstallRegistryKey
+
+
+## Notes
+
+- There is [a known bug](https://github.com/chocolatey-community/chocolatey-coreteampackages/issues/784) in the function `Get-AppInstallLocation` with parameter `$AppNamePattern` which is internally used both as wildcard and regex patterns. This usually doesn't create any problems, but may do so if application contains regex symbols in the name, such as [notepad++](https://github.com/chocolatey-community/chocolatey-coreteampackages/issues/1198).
+
+
diff --git a/src/chocolatey-core.extension/chocolatey-core.extension.nuspec b/src/chocolatey-core.extension/chocolatey-core.extension.nuspec
new file mode 100644
index 0000000..437b451
--- /dev/null
+++ b/src/chocolatey-core.extension/chocolatey-core.extension.nuspec
@@ -0,0 +1,30 @@
+
+
+
+
+ chocolatey-core.extension
+ 1.3.5.1
+ Chocolatey Core Extensions
+ Helper functions extending core choco functionality
+ chocolatey
+ chocolatey-community
+
+This package provides helper functions installed as a Chocolatey extension.
+These functions may be used in Chocolatey install/uninstall scripts by declaring this package a dependency in your package's nuspec.
+
+ chocolatey core extension admin
+ https://github.com/chocolatey/chocolatey-coreteampackages
+
+ © 2016 Chocolatey Core Team Package Contributors
+ https://github.com/chocolatey/chocolatey-coreteampackages/blob/master/LICENSE.md
+ false
+ https://github.com/chocolatey/chocolatey-coreteampackages
+ https://github.com/chocolatey/chocolatey-coreteampackages/tree/master/extensions/chocolatey-core.extension
+ https://github.com/chocolatey/chocolatey-coreteampackages/tree/master/extensions/chocolatey-core.extension/README.md
+ https://github.com/chocolatey/chocolatey-coreteampackages/issues
+ https://github.com/chocolatey/chocolatey-coreteampackages/tree/master/extensions/chocolatey-core.extension/CHANGELOG.md
+
+
+
+
+
diff --git a/src/chocolatey-core.extension/extensions/Get-AppInstallLocation.ps1 b/src/chocolatey-core.extension/extensions/Get-AppInstallLocation.ps1
new file mode 100644
index 0000000..ef28520
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-AppInstallLocation.ps1
@@ -0,0 +1,77 @@
+. "$PSScriptRoot\Get-UninstallRegistryKey.ps1"
+<#
+.SYNOPSIS
+ Get application install location
+
+.DESCRIPTION
+ Function tries to find install location in multiple places. It returns $null if all fail. The following
+ locations are tried:
+ - local and machine (x32 & x64) various Uninstall keys
+ - x32 & x64 Program Files up to the 2nd level of depth
+ - native commands available via PATH
+ - locale and machine registry key SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
+
+ Use Verbose parameter to see which of the above locations was used for the result, if any.
+
+.EXAMPLE
+ PS> Get-AppInstallLocation choco
+
+ Returns the install location of the application 'choco'.
+
+.OUTPUTS
+ [String] or $null
+#>
+function Get-AppInstallLocation {
+ [CmdletBinding()]
+ param(
+ # Regular expression pattern
+ [ValidateNotNullOrEmpty()]
+ [string] $AppNamePattern,
+
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ function strip($path) { if ($path.EndsWith('\')) { return $path -replace '.$' } else { $path } }
+
+ function is_dir( $path ) { $path -and (gi $path -ea 0).PsIsContainer -eq $true }
+
+ $ErrorActionPreference = "SilentlyContinue"
+
+ Write-Verbose "Trying local and machine (x32 & x64) Uninstall keys"
+ [array] $key = Get-UninstallRegistryKey $AppNamePattern
+ if ($key.Count -eq 1) {
+ Write-Verbose "Trying Uninstall key property 'InstallLocation'"
+ $location = $key.InstallLocation
+ if (is_dir $location) { return strip $location }
+
+ Write-Verbose "Trying Uninstall key property 'UninstallString'"
+ $location = $key.UninstallString
+ if ($location) { $location = $location.Replace('"', '') | Split-Path }
+ if (is_dir $location) { return strip $location }
+
+ Write-Verbose "Trying Uninstall key property 'DisplayIcon'"
+ $location = $key.DisplayIcon
+ if ($location) { $location = Split-Path $location }
+ if (is_dir $location) { return strip $location }
+ } else { Write-Verbose "Found $($key.Count) keys, aborting this method" }
+
+ $dirs = $Env:ProgramFiles, "$Env:ProgramFiles\*\*"
+ if (Get-ProcessorBits 64) { $dirs += ${ENV:ProgramFiles(x86)}, "${ENV:ProgramFiles(x86)}\*\*" }
+ Write-Verbose "Trying Program Files with 2 levels depth: $dirs"
+ $location = (ls $dirs | ? {$_.PsIsContainer}) -match $AppNamePattern | select -First 1 | % {$_.FullName}
+ if (is_dir $location) { return strip $location }
+
+ Write-Verbose "Trying native commands on PATH"
+ $location = (Get-Command -CommandType Application) -match $AppNamePattern | select -First 1 | % { Split-Path $_.Source }
+ if (is_dir $location) { return strip $location }
+
+ $appPaths = "\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths"
+ Write-Verbose "Trying Registry: $appPaths"
+ $location = (ls "HKCU:\$appPaths", "HKLM:\$appPaths") -match $AppNamePattern | select -First 1
+ if ($location) { $location = Split-Path $location }
+ if (is_dir $location) { return strip $location }
+
+ Write-Verbose "No location found"
+}
diff --git a/src/chocolatey-core.extension/extensions/Get-AvailableDriveLetter.ps1 b/src/chocolatey-core.extension/extensions/Get-AvailableDriveLetter.ps1
new file mode 100644
index 0000000..dad71b2
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-AvailableDriveLetter.ps1
@@ -0,0 +1,50 @@
+<#
+.SYNOPSIS
+ Get a 'free' drive letter
+
+.DESCRIPTION
+ Get a not yet in-use drive letter that can be used for mounting
+
+.EXAMPLE
+ Get-AvailableDriveLetter
+
+.EXAMPLE
+ Get-AvailableDriveLetter 'X'
+ (do not return X, even if it'd be the next choice)
+
+.INPUTS
+ specific drive letter(s) that will be excluded as potential candidates
+
+.OUTPUTS
+ System.String (single drive-letter character)
+
+.LINK
+ http://stackoverflow.com/questions/12488030/getting-a-free-drive-letter/29373301#29373301
+#>
+function Get-AvailableDriveLetter {
+ param (
+ [char[]]$ExcludedLetters,
+
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ $Letter = [int][char]'C'
+ $i = @()
+
+ #getting all the used Drive letters reported by the Operating System
+ $(Get-PSDrive -PSProvider filesystem) | %{$i += $_.name}
+
+ #Adding the excluded letter
+ $i+=$ExcludedLetters
+
+ while($i -contains $([char]$Letter)){$Letter++}
+
+ if ($Letter -gt [char]'Z') {
+ throw "error: no drive letter available!"
+ }
+ Write-Verbose "available drive letter: '$([char]$Letter)'"
+ Return $([char]$Letter)
+}
+
diff --git a/src/chocolatey-core.extension/extensions/Get-EffectiveProxy.ps1 b/src/chocolatey-core.extension/extensions/Get-EffectiveProxy.ps1
new file mode 100644
index 0000000..5356f9d
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-EffectiveProxy.ps1
@@ -0,0 +1,66 @@
+<#
+.SYNOPSIS
+ Get the current proxy using several methods
+
+.DESCRIPTION
+ Function tries to find the current proxy using several methods, in the given order:
+ - $env:chocolateyProxyLocation variable
+ - $env:http_proxy environment variable
+ - IE proxy
+ - Chocolatey config
+ - Winhttp proxy
+ - WebClient proxy
+
+ Use Verbose parameter to see which of the above locations was used for the result, if any.
+ The function currently doesn't handle the proxy username and password.
+
+.OUTPUTS
+ [String] in the form of http://:
+#>
+function Get-EffectiveProxy(){
+
+ # Try chocolatey proxy environment vars
+ if ($env:chocolateyProxyLocation) {
+ Write-Verbose "Using `$Env:chocolateyProxyLocation"
+ return $env:chocolateyProxyLocation
+ }
+
+ # Try standard Linux variable
+ if ($env:http_proxy) {
+ Write-Verbose "Using `$Env:http_proxy"
+ return $env:http_proxy
+ }
+
+ # Try to get IE proxy
+ $key = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings"
+ $r = Get-ItemProperty $key
+ if ($r.ProxyEnable -and $r.ProxyServer) {
+ Write-Verbose "Using IE proxy settings"
+ return "http://" + $r.ProxyServer
+ }
+
+ # Try chocolatey config file
+ [xml] $cfg = gc $env:ChocolateyInstall\config\chocolatey.config
+ $p = $cfg.chocolatey.config | % { $_.add } | ? { $_.key -eq 'proxy' } | select -Expand value
+ if ($p) {
+ Write-Verbose "Using choco config proxy"
+ return $p
+ }
+
+ # Try winhttp proxy
+ (netsh.exe winhttp show proxy) -match 'Proxy Server\(s\)' | set proxy
+ $proxy = $proxy -split ' :' | select -Last 1
+ $proxy = $proxy.Trim()
+ if ($proxy) {
+ Write-Verbose "Using winhttp proxy server"
+ return "http://" + $proxy
+ }
+
+ # Try using WebClient
+ $url = "http://chocolatey.org"
+ $client = New-Object System.Net.WebClient
+ if ($client.Proxy.IsBypassed($url)) { return $null }
+
+ Write-Verbose "Using WebClient proxy"
+ return "http://" + $client.Proxy.GetProxy($url).Authority
+}
diff --git a/src/chocolatey-core.extension/extensions/Get-PackageCacheLocation.ps1 b/src/chocolatey-core.extension/extensions/Get-PackageCacheLocation.ps1
new file mode 100644
index 0000000..3cd5291
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-PackageCacheLocation.ps1
@@ -0,0 +1,40 @@
+<#
+.SYNOPSIS
+ Get temporary location for the package based on its name and version.
+
+.DESCRIPTION
+ The function returns package cache directory within $Env:TEMP. It will not create the directory
+ if it doesn't exist.
+
+ This function is useful when you have to obtain the file using `Get-ChocolateyWebFile` in order
+ to perform certain installation steps that other helpers can't do.
+
+.EXAMPLE
+ Get-PackageCacheLocation
+
+.OUTPUTS
+ [String]
+
+.LINKS
+ Get-ChocolateyWebFile
+#>
+function Get-PackageCacheLocation {
+ [CmdletBinding()]
+ param (
+ # Name of the package, by default $Env:ChocolateyPackageName
+ [string] $Name = $Env:ChocolateyPackageName,
+ # Version of the package, by default $Env:ChocolateyPackageVersion
+ [string] $Version = $Env:ChocolateyPackageVersion,
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ if (!$Name) { Write-Warning 'Environment variable $Env:ChocolateyPackageName is not set' }
+ $res = Join-Path $Env:TEMP $Name
+
+ if (!$Version) { Write-Warning 'Environment variable $Env:ChocolateyPackageVersion is not set' }
+ $res = Join-Path $res $Version
+
+ $res
+}
diff --git a/src/chocolatey-core.extension/extensions/Get-PackageParameters.ps1 b/src/chocolatey-core.extension/extensions/Get-PackageParameters.ps1
new file mode 100644
index 0000000..5b77ff0
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-PackageParameters.ps1
@@ -0,0 +1,35 @@
+<#
+.SYNOPSIS
+ Parses parameters of the package
+
+.EXAMPLE
+ Get-PackageParameters "/Shortcut /InstallDir:'c:\program files\xyz' /NoStartup" | set r
+ if ($r.Shortcut) {... }
+ Write-Host $r.InstallDir
+
+.OUTPUTS
+ [HashTable]
+#>
+function Get-PackageParameters {
+ [CmdletBinding()]
+ param(
+ [string] $Parameters = $Env:ChocolateyPackageParameters,
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ $res = @{}
+
+ $re = "\/([a-zA-Z0-9]+)(:[`"'].+?[`"']|[^ ]+)?"
+ $results = $Parameters | Select-String $re -AllMatches | select -Expand Matches
+ foreach ($m in $results) {
+ if (!$m) { continue } # must because of posh 2.0 bug: https://github.com/chocolatey/chocolatey-coreteampackages/issues/465
+
+ $a = $m.Value -split ':'
+ $opt = $a[0].Substring(1); $val = $a[1..100] -join ':'
+ if ($val -match '^(".+")|(''.+'')$') {$val = $val -replace '^.|.$'}
+ $res[ $opt ] = if ($val) { $val } else { $true }
+ }
+ $res
+}
diff --git a/src/chocolatey-core.extension/extensions/Get-UninstallRegistryKey.ps1 b/src/chocolatey-core.extension/extensions/Get-UninstallRegistryKey.ps1
new file mode 100644
index 0000000..6431f44
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-UninstallRegistryKey.ps1
@@ -0,0 +1,102 @@
+<#
+.SYNOPSIS
+ Retrieve registry key(s) for system-installed applications from an exact or wildcard search.
+
+.DESCRIPTION
+ This function will attempt to retrieve a matching registry key for an already installed application,
+ usually to be used with a chocolateyUninstall.ps1 automation script.
+
+ The function also prevents `Get-ItemProperty` from failing when handling wrongly encoded registry keys.
+
+.PARAMETER SoftwareName
+ Part or all of the Display Name as you see it in Programs and Features.
+ It should be enough to be unique.
+ The syntax follows the rules of the PowerShell `-like` operator, so the `*` character is interpreted
+ as a wildcard, which matches any (zero or more) characters.
+
+ If the display name contains a version number, such as "Launchy (2.5)", it is recommended you use a
+ fuzzy search `"Launchy (*)"` (the wildcard `*`) so if Launchy auto-updates or is updated outside
+ of chocolatey, the uninstall script will not fail.
+
+ Take care not to abuse fuzzy/glob pattern searches. Be conscious of programs that may have shared
+ or common root words to prevent overmatching. For example, "SketchUp*" would match two keys with software
+ names "SketchUp 2016" and "SketchUp Viewer" that are different programs released by the same company.
+
+.PARAMETER IgnoredArguments
+ Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+
+.INPUTS
+ System.String
+
+.OUTPUTS
+ PSCustomObject
+
+.EXAMPLE
+ [array]$key = Get-UninstallRegistryKey -SoftwareName "VLC media player"
+ $key.UninstallString
+
+ Exact match: software name in Programs and Features is "VLC media player"
+
+.EXAMPLE
+ [array]$key = Get-UninstallRegistryKey -SoftwareName "Gpg4win (*)"
+ $key.UninstallString
+
+ Version match: software name is "Gpg4Win (2.3.0)"
+
+.EXAMPLE
+ [array]$key = Get-UninstallRegistryKey -SoftwareName "SketchUp [0-9]*"
+ $key.UninstallString
+
+ Version match: software name is "SketchUp 2016"
+ Note that the similar software name "SketchUp Viewer" would not be matched.
+
+.LINK
+ Uninstall-ChocolateyPackage
+#>
+function Get-UninstallRegistryKey {
+ [CmdletBinding()]
+ param(
+ [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
+ [ValidateNotNullOrEmpty()]
+ [string] $SoftwareName,
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+ Write-Debug "Running 'Get-UninstallRegistryKey' for `'$env:ChocolateyPackageName`' with SoftwareName:`'$SoftwareName`'";
+
+ $ErrorActionPreference = 'Stop'
+ $local_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
+ $machine_key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
+ $machine_key6432 = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
+
+ Write-Verbose "Retrieving all uninstall registry keys"
+ [array]$keys = Get-ChildItem -Path @($machine_key6432, $machine_key, $local_key) -ea 0
+ Write-Debug "Registry uninstall keys on system: $($keys.Count)"
+
+ Write-Debug "Error handling check: `'Get-ItemProperty`' fails if a registry key is encoded incorrectly."
+ [int]$maxAttempts = $keys.Count
+ for ([int]$attempt = 1; $attempt -le $maxAttempts; $attempt++)
+ {
+ $success = $false
+
+ $keyPaths = $keys | Select-Object -ExpandProperty PSPath
+ try {
+ [array]$foundKey = Get-ItemProperty -Path $keyPaths -ea 0 | ? { $_.DisplayName -like $SoftwareName }
+ $success = $true
+ } catch {
+ Write-Debug "Found bad key."
+ foreach ($key in $keys){ try{ Get-ItemProperty $key.PsPath > $null } catch { $badKey = $key.PsPath }}
+ Write-Verbose "Skipping bad key: $badKey"
+ [array]$keys = $keys | ? { $badKey -NotContains $_.PsPath }
+ }
+
+ if ($success) { break; }
+ if ($attempt -eq 10) {
+ Write-Warning "Found more than 10 bad registry keys. Run command again with `'--verbose --debug`' for more info."
+ Write-Debug "Each key searched should correspond to an installed program. It is very unlikely to have more than a few programs with incorrectly encoded keys, if any at all. This may be indicative of one or more corrupted registry branches."
+ }
+ }
+
+ Write-Debug "Found $($foundKey.Count) uninstall registry key(s) with SoftwareName:`'$SoftwareName`'";
+ return $foundKey
+}
diff --git a/src/chocolatey-core.extension/extensions/Get-WebContent.ps1 b/src/chocolatey-core.extension/extensions/Get-WebContent.ps1
new file mode 100644
index 0000000..6e9287e
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Get-WebContent.ps1
@@ -0,0 +1,67 @@
+<#
+.SYNOPSIS
+ Download file with choco internals
+
+.DESCRIPTION
+ This function will download a file from specified url and return it as a string.
+ This command should be a replacement for ubiquitous WebClient in install scripts.
+
+ The benefit of using this command instead of WebClient is that it correctly handles
+ system or explicit proxy.
+
+.EXAMPLE
+ PS C:\> $s = Get-WebContent "http://example.com"
+ PS C:\> $s -match 'Example Domain'
+ True
+
+ First command downloads html content from http://example.com and stores it in $s.
+ Now you can parse and match it as a string.
+
+.EXAMPLE
+ PS C:\> $opts = @{ Headers = @{ Referer = 'http://google.com' } }
+ PS C:\> $s = Get-WebContent -url "http://example.com" -options $opts
+
+ You can set headers for http request this way.
+
+.INPUTS
+ None
+
+.OUTPUTS
+ System.String
+
+.NOTES
+ This function can only return string content.
+ If you want to download a binary content, please use Get-WebFile.
+
+.LINK
+ Get-WebFile
+#>
+function Get-WebContent {
+ [CmdletBinding()]
+ param(
+ # Url to download
+ [string]$Url,
+
+ # Additional options for http request.For now only Headers property supported.
+ [hashtable]$Options,
+
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ $filePath = get_temp_filepath
+ Get-WebFile -Url $Url -FileName $filePath -Options $Options 3>$null
+
+ $fileContent = Get-Content $filePath -ReadCount 0 | Out-String
+ Remove-Item $filePath
+
+ $fileContent
+}
+
+function get_temp_filepath() {
+ $tempDir = Get-PackageCacheLocation
+ $fileName = [System.IO.Path]::GetRandomFileName()
+ Join-Path $tempDir $fileName
+}
+
diff --git a/src/chocolatey-core.extension/extensions/Register-Application.ps1 b/src/chocolatey-core.extension/extensions/Register-Application.ps1
new file mode 100644
index 0000000..c525db6
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Register-Application.ps1
@@ -0,0 +1,52 @@
+<#
+.SYNOPSIS
+ Register application in the system
+
+.DESCRIPTION
+ The function will register application in the system using App Paths registry key so that you
+ can start it by typing its registred name in the Windows Start menu on using run dialog (Win + R).
+
+ To make applications available in the shell, add to your profile Set-AppKeyAliases function.
+
+.EXAMPLE
+ Register-Application 'c:\windows\notepad.exe'
+
+ Register application using name derived from its file name.
+
+.EXAMPLE
+ Register-Application 'c:\windows\notepad.exe' -Name ntp
+
+ Register application using explicit name.
+
+.LINK
+ Set-AppKeyAliases - https://github.com/majkinetor/posh/blob/master/MM_Admin/Set-AppKeyAliases.ps1
+ Application Registration - https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx
+
+#>
+function Register-Application{
+ [CmdletBinding()]
+ param(
+ # Full path of the executable to register.
+ [Parameter(Mandatory=$true)]
+ [string]$ExePath,
+
+ # Optional name to register with. By default exe name will be used.
+ [string]$Name,
+
+ # Register application only for the current user. By default registration is for the machine.
+ [switch]$User,
+
+ # Allows splatting with arguments that do not apply and future expansion. Do not use directly.
+ [parameter(ValueFromRemainingArguments = $true)]
+ [Object[]] $IgnoredArguments
+ )
+
+ if (!(Test-Path $ExePath)) { throw "Path doesn't exist: $ExePath" }
+ if (!$Name) { $Name = Split-Path $ExePath -Leaf } else { $Name = $Name + '.exe' }
+
+ $appPathKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\$Name"
+ if ($User) { $appPathKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\$Name" }
+
+ If (!(Test-Path $AppPathKey)) { New-Item "$AppPathKey" | Out-Null }
+ Set-ItemProperty -Path $AppPathKey -Name "(Default)" -Value $ExePath
+}
diff --git a/src/chocolatey-core.extension/extensions/Remove-Process.ps1 b/src/chocolatey-core.extension/extensions/Remove-Process.ps1
new file mode 100644
index 0000000..857e0c3
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/Remove-Process.ps1
@@ -0,0 +1,103 @@
+<#
+.SYNOPSIS
+ Ensure that process is stopped in reliable way
+
+.DESCRIPTION
+ Close the processes matching filters gracefully first, then forcefully.
+ If no process is found, function will simply do and return nothing.
+
+.EXAMPLE
+ notepad; Remove-Process notepad -PathFilter 'system32'
+
+ Close main window of notepad that has 'system32' word in its path
+
+.EXAMPLE
+ Remove-Process notepad -WaitFor 30
+ notepad; notepad #in another shell
+
+ Close all instances of notepad but wait for them up to 30 seconds to start
+
+.OUTPUTS
+ Array of closeed processes with details about each one.
+
+.NOTES
+ https://github.com/chocolatey-community/chocolatey-coreteampackages/issues/1364
+#>
+
+function Remove-Process {
+ param(
+ # RegEx expression of process name, returned by Get-Process function
+ [string] $NameFilter,
+
+ # RegEx expression of process path, returned by Get-Process function
+ [string] $PathFilter,
+
+ # Wait for process to start number of seconds
+ # Function will try to find process every second until timeout.
+ [int] $WaitFor,
+
+ # Close/Kill child processes, by default they are filtered out as
+ # parent-child relationship usually have its own heartbeat feature
+ [switch] $WithChildren
+ )
+
+ function getp {
+ foreach ($p in Get-Process) {
+ $b1 = if ($NameFilter) { $p.ProcessName -match $NameFilter }
+ $b2 = if ($PathFilter) { $p.Path -match $PathFilter }
+ $b = if (($b1 -ne $null) -and ($b2 -ne $null)) { $b1 -and $b2 } else { $b1 -or $b2 }
+ if (!$b) { continue }
+
+ $w = Get-WmiObject win32_process -Filter "ProcessId = $($p.Id)"
+ [PSCustomObject]@{
+ Id = $p.Id
+ ParentId = $w.ParentProcessId
+ Name = $p.ProcessName
+ Path = $p.Path
+ CommandLine = $w.CommandLine
+ Process = $p
+ Username = $w.GetOwner().Domain + "\"+ $w.GetOwner().User
+ }
+ }
+ }
+
+ $proc = getp
+ if (!$proc -and $WaitFor) {
+ Write-Verbose "Waiting for process up to $WaitFor seconds"
+ for ($i=0; $i -lt $WaitFor; $i++) { Start-Sleep 1; $proc = getp; if ($proc) {break} }
+ }
+ if (!$proc) { return }
+
+ # Process might spawn multiple children, typical for browsers; remove all children as parent will handle them
+ if (!$WithChildren) {
+ Write-Verbose "Remove all children processes"
+ $proc = $proc | ? { $proc.Id -notcontains $_.ParentId }
+ }
+
+ foreach ($p in $proc) {
+ Write-Verbose "Trying to close app '$($p.Name)' run by user '$($p.Username)'"
+
+ if ( $p.Process.CloseMainWindow() ) {
+ # wait for app to shut down for some time, max 5s
+ for ($i=0; $i -lt 5; $i++) {
+ Start-Sleep 1
+ $p2 = ps -PID $p.id -ea 0
+ if (!$p2) { break }
+ }
+ }
+
+ # Return value of CloseMainWindow() 'True' is not reliable
+ # so if process is still active kill it
+ $p2 = ps -PID $p.id -ea 0
+ if (($p.Process.Name -eq $p2.Name) -and ($p.Process.StartTime -eq $p2.StartTime)) {
+ $p | Stop-Process -ea STOP
+ Start-Sleep 1 # Running to fast here still gets the killed process in next line
+ }
+
+ $p2 = ps -PID $p.id -ea 0
+ if (($p.Process.Name -eq $p2.Name) -and ($p.Process.StartTime -eq $p2.StartTime)) {
+ Write-Warning "Process '$($p.Name)' run by user '$($p.Username)' can't be closed"
+ }
+ }
+ $proc
+}
diff --git a/src/chocolatey-core.extension/extensions/chocolatey-core.psm1 b/src/chocolatey-core.extension/extensions/chocolatey-core.psm1
new file mode 100644
index 0000000..47f413a
--- /dev/null
+++ b/src/chocolatey-core.extension/extensions/chocolatey-core.psm1
@@ -0,0 +1,15 @@
+# Export functions that start with capital letter, others are private
+# Include file names that start with capital letters, ignore others
+$ScriptRoot = Split-Path $MyInvocation.MyCommand.Definition
+
+$pre = Get-ChildItem Function:\*
+Get-ChildItem "$ScriptRoot\*.ps1" |
+ Where-Object { $_.Name -cmatch '^[A-Z]+' } |
+ ForEach-Object { . $_ }
+$post = Get-ChildItem Function:\*
+$funcs = Compare-Object $pre $post |
+ Select-Object -ExpandProperty InputObject |
+ Select-Object -ExpandProperty Name
+$funcs |
+ Where-Object { $_ -cmatch '^[A-Z]+'} |
+ ForEach-Object { Export-ModuleMember -Function $_ }
\ No newline at end of file