Invoke-Web 4.2 poshcode ID 3913 (by Joel Bennett) is corrupt?
I am downloading Invoke-Web 4.2 by Joel Bennett http://poshcode.org/3913 using http://poshcode.org/get/3913 but powershell 4 and 3 give me loads of errrors.
Is this file corrupt? my md5sum of it is:
md5sum "Invoke-Web 4.2.ps1"
ad83ec384a54b7431f8f99781180f8b5 *Invoke-Web 4.2.ps1
Both powershell 3.0 & 4.0 gives me the following errors if I run it:
At C:\temp\Invoke-Web 4.2.ps1:147 char:57
+ [ValidateScript({if(!($Credential -or $WebSession){ throw
"ForceBasicAuth ...
+ ~
Unexpected token '{' in expression or statement.
At C:\temp\Invoke-Web 4.2.ps1:147 char:57
+ [ValidateScript({if(!($Credential -or $WebSession){ throw
"ForceBasicAuth ...
+ ~
Missing closing ')' after expression in 'if' statement.
At C:\temp\Invoke-Web 4.2.ps1:147 char:124
+ ... meter be set"} else { $true }})]
+ ~~~~
Unexpected token 'else' in expression or statement.
At C:\temp\Invoke-Web 4.2.ps1:52 char:21
+ function Invoke-Web {
+ ~
Missing closing '}' in statement block.
At C:\temp\Invoke-Web 4.2.ps1:377 char:1
+ }}
+ ~
Unexpected token '}' in expression or statement.
At C:\temp\Invoke-Web 4.2.ps1:377 char:2
+ }}
+ ~
Unexpected token '}' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
I tried quite long and hard to 'fix' the places it complained about, but there is one I just can't figure out, and maybe that is because I made mistakes when fixing the others?
This is the only error I am left with:
At C:\temp\Invoke-Web 4.2_nearly-fixed.ps1:52 char:21
+ function Invoke-Web {
+ ~
Missing closing '}' in statement block.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
My "nearly fixed" version now looks like this.
function ConvertTo-Dictionary {
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="Hashtable")]
[Hashtable[]]$Hashtable,
[Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="WebHeaders")]
[System.Collections.Specialized.NameObjectCollectionBase]$Headers,
[Parameter(Mandatory=$true,ParameterSetName="Hashtable")]
[Type]$TKey,
[Parameter(Mandatory=$true,ParameterSetName="Hashtable")]
[Type]$Tvalue
)
begin {
switch($PSCmdlet.ParameterSetName) {
"Hashtable" {
$dictionary = New-Object "System.Collections.Generic.Dictionary[[$($TKey.FullName)],[$($TValue.FullName)]]"
}
"WebHeaders" {
$dictionary = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
}
}
}
process {
switch($PSCmdlet.ParameterSetName) {
"Hashtable" {
foreach($ht in $Hashtable) {
foreach($key in $ht.Keys) {
$dictionary.Add( $key, $ht.$key )
}
}
}
"WebHeaders" {
foreach($key in $Headers.AllKeys) {
$dictionary.Add($key, $Headers[$key])
}
}
}
}
end { return $dictionary }
}
function ConvertFrom-Dictionary {
[CmdletBinding()]
param($Dictionary, [Switch]$Encode)
foreach($key in $Dictionary.Keys) {
"{0} = {1}" -f $key, $( if($Encode) { [System.Net.WebUtility]::UrlEncode( $Dictionary.$key ) } else { $Dictionary.$key } )
}
}
## Get-WebFile (aka wget for PowerShell)
function Invoke-Web {
#.Synopsis
# Downloads a file or page from the web, or sends web API posts/requests
#.Description
# Creates an HttpWebRequest to download a web file or post data
#.Example
# Invoke-Web http://PoshCode.org/PoshCode.psm1
#
# Downloads the latest version of the PoshCode module to the current directory
#.Example
# Invoke-Web http://PoshCode.org/PoshCode.psm1 ~\Documents\WindowsPowerShell\Modules\PoshCode\
#
# Downloads the latest version of the PoshCode module to the default PoshCode module directory...
#.Example
# $RssItems = @(([xml](Invoke-Web http://poshcode.org/api/ -passthru)).rss.channel.GetElementsByTagName("item"))
#
# Returns the most recent items from the PoshCode.org RSS feed
#.Notes
# History:
# v4.2 - Fixed bugs when sending content in the body.
# - Added -ForceBasicAuth to allow client-side to specify Authentication: Basic header.
# v4.1 - Reworked most of it with PowerShell 3's Invoke-WebRequest as inspiration
# - Added a bunch of parameters, the ability to do PUTs etc., and session/cookie persistence
# - Did NOT parse the return code and get you the FORMs the way PowerShell 3 does -- upgrade! ;)
# v3.12 - Added full help
# v3.9 - Fixed and replaced the Set-DownloadFlag
# v3.7 - Removed the Set-DownloadFlag code because it was throwing on Windows 7:
# "Attempted to read or write protected memory."
# v3.6.6 Add UserAgent calculation and parameter
# v3.6.5 Add file-name guessing and cleanup
# v3.6 - Add -Passthru switch to output TEXT files
# v3.5 - Add -Quiet switch to turn off the progress reports ...
# v3.4 - Add progress report for files which don't report size
# v3.3 - Add progress report for files which report their size
# v3.2 - Use the pure Stream object because StreamWriter is based on TextWriter:
# it was messing up binary files, and making mistakes with extended characters in text
# v3.1 - Unwrap the filename when it has quotes around it
# v3 - rewritten completely using HttpWebRequest + HttpWebResponse to figure out the file name, if possible
# v2 - adds a ton of parsing to make the output pretty
# added measuring the scripts involved in the command, (uses Tokenizer)
[CmdletBinding(DefaultParameterSetName="NoSession")]
param(
# The URL of the file/page to download
[Parameter(Mandatory=$true,Position=0)]
[System.Uri][Alias("Url")]$Uri # = (Read-Host "The URL to download")
,
# Specifies the body of the request. The body is the content of the request that follows the headers.
# You can also pipe a request body to Invoke-WebRequest
# Note that you should probably set the ContentType if you're setting the Body
[Parameter(ValueFromPipeline=$true)]
$Body
,
# Specifies the content type of the web request, such as "application/x-www-form-urlencoded" (defaults to "application/x-www-form-urlencoded" if the Body is set to a hashtable, dictionary, or other NameValueCollection)
[String]$ContentType
,
# Specifies the client certificate that is used for a secure web request. Enter a variable that contains a certificate or a command or expression that gets the certificate.
# To find a certificate, use Get-PfxCertificate or use the Get-ChildItem cmdlet in the Certificate (Cert:) drive. If the certificate is not valid or does not have sufficient authority, the command fails.
[System.Security.Cryptography.X509Certificates.X509Certificate[]]
$Certificate
,
# Sends the results to the specified output file. Enter a path and file name. If you omit the path, the default is the current location.
# By default, Invoke-WebRequest returns the results to the pipeline. To send the results to a file and to the pipeline, use the Passthru parameter.
[string]$OutFile
,
# Leave the file unblocked instead of blocked
[Switch]$Unblocked
,
# Rather than saving the downloaded content to a file, output it.
# This is for text documents like web pages and rss feeds, and allows you to avoid temporarily caching the text in a file.
[switch]$Passthru
,
# Supresses the Write-Progress during download
[switch]$Quiet
,
# Specifies a name for the session variable. Enter a variable name without the dollar sign ($) symbol.
# When you use the session variable in a web request, the variable is populated with a WebRequestSession object.
# You cannot use the SessionVariable and WebSession parameters in the same command
[Parameter(Mandatory=$true,ParameterSetName="CreateSession")]
[String]$SessionVariable
,
# Specifies a web request session to store data for subsequent requests.
# You cannot use the SessionVariable and WebSession parameters in the same command
[Parameter(Mandatory=$true,ParameterSetName="UseSession")]
$WebSession
,
# Pass the default credentials
[switch]$UseDefaultCredentials
,
# Specifies a user account that has permission to send the request. The default is the current user.
# Type a user name, such as "User01" or "Domain01\User01", or enter a PSCredential object, such as one generated by the Get-Credential cmdlet.
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
[Alias("")]$Credential = [System.Management.Automation.PSCredential]::Empty
,
# Specifies that Authorization: Basic should always be sent. Requires $Credential to be set, and should only be used with https
[ValidateScript({if(!($Credential -or $WebSession)){ throw "ForceBasicAuth requires the Credential parameter be set"} else { $true }})]
$ForceBasicAuth
,
# Sets the KeepAlive value in the HTTP header to False. By default, KeepAlive is True. KeepAlive establishes a persistent connection to the server to facilitate subsequent requests.
$DisableKeepAlive
,
# Specifies the headers for the web request. Enter a hash table or dictionary.
[System.Collections.IDictionary]$Headers
,
# Determines how many times Windows PowerShell redirects a connection to an alternate Uniform Resource Identifier (URI) before the connection fails.
# Our default value is 5 (but .Net's default is 50). A value of 0 (zero) prevents all redirection.
[int]$MaximumRedirection = 5
,
# Specifies the method used for the web request. Valid values are Default, Delete, Get, Head, Options, Post, Put, and Trace. Default value is Get.
[ValidateSet("Default", "Delete", "Get", "Head", "Options", "Post", "Put", "Trace")]
[String]$Method = "Get"
,
# Uses a proxy server for the request, rather than connecting directly to the Internet resource. Enter the URI of a network proxy server.
# Note: if you have a default proxy configured in your internet settings, there is no need to set it here.
[Uri]$Proxy
,
# Pass the default credentials to the Proxy
[switch]$ProxyUseDefaultCredentials
,
# Pass specific credentials to the Proxy
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$ProxyCredential= [System.Management.Automation.PSCredential]::Empty
,
# Text to include at the front of the UserAgent string
[string]$UserAgent = "Mozilla/5.0 (Windows NT; Windows NT $([Environment]::OSVersion.Version.ToString(2)); $PSUICulture) WindowsPowerShell/$($PSVersionTable.PSVersion.ToString(2)); PoshCode/4.0; http://PoshCode.org"
)
process {
Write-Verbose "Downloading '$Uri'"
$EAP,$ErrorActionPreference = $ErrorActionPreference, "Stop"
$request = [System.Net.HttpWebRequest]::Create($Uri)
if($DebugPreference -ne "SilentlyContinue") {
Set-Variable WebRequest -Scope 2 -Value $request
}
$ErrorActionPreference = $EAP
# Not everything is a GET request ...
$request.Method = $Method.ToUpper()
# Now that we have a web request, we'll use the session values first if we have any
if($WebSession) {
$request.CookieContainer = $WebSession.Cookies
$request.Headers = $WebSession.Headers
if($WebSession.UseDefaultCredentials) {
$request.UseDefaultCredentials
} elseif($WebSession.Credentials) {
$request.Credentials = $WebSession.Credentials
}
$request.ClientCertificates = $WebSession.Certificates
$request.UserAgent = $WebSession.UserAgent
$request.Proxy = $WebSession.Proxy
$request.MaximumAutomaticRedirections = $WebSession.MaximumRedirection
} else {
$request.CookieContainer = $Cookies = New-Object System.Net.CookieContainer
}
# And override session values with user values if they provided any
$request.UserAgent = $UserAgent
$request.MaximumAutomaticRedirections = $MaximumRedirection
$request.KeepAlive = !$DisableKeepAlive
# Authentication normally uses EITHER credentials or certificates, but what do I know ...
if($Certificate) {
$request.ClientCertificates.AddRange($Certificate)
}
if($UseDefaultCredentials) {
$request.UseDefaultCredentials = $true
} elseif($Credential -ne [System.Management.Automation.PSCredential]::Empty) {
$request.Credentials = $Credential.GetNetworkCredential()
}
# You don't have to specify a proxy to specify proxy credentials (maybe your default proxy takes creds)
if($Proxy) { $request.Proxy = New-Object System.Net.WebProxy $Proxy }
if($request.Proxy -ne $null) {
if($ProxyUseDefaultCredentials) {
$request.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
} elseif($ProxyCredentials -ne [System.Management.Automation.PSCredential]::Empty) {
$request.Proxy.Credentials = $ProxyCredentials
}
}
if($ForceBasicAuth) {
if(!$request.Credentials) {
throw "ForceBasicAuth requires Credentials!"
}
}
$request.Headers.Add('Authorization', 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($request.Credentials.UserName+":"+$request.Credentials.Password )));
}
if($SessionVariable) {
Set-Variable $SessionVariable -Scope 1 -Value $WebSession
}
if($Headers) {
foreach($h in $Headers.Keys) {
$request.Headers.Add($h, $Headers[$h])
}
}
if($Body) {
if($Body -is [System.Collections.IDictionary] -or $Body -is [System.Collections.Specialized.NameObjectCollectionBase]) {
if(!$ContentType) {
$ContentType = "application/x-www-form-urlencoded"
}
[String]$Body = ConvertFrom-Dictionary $Body -Encode:$($ContentType -eq "application/x-www-form-urlencoded")
} else {
$Body = $Body | Out-String
}
$encoding = New-Object System.Text.ASCIIEncoding
$bytes = $encoding.GetBytes($Body);
$request.ContentType = $ContentType
$request.ContentLength = $bytes.Length
$writer = $request.GetRequestStream();
$writer.Write($bytes, 0, $bytes.Length)
$writer.Close()
}
try {
$response = $request.GetResponse();
if($DebugPreference -ne "SilentlyContinue") {
Set-Variable WebResponse -Scope 2 -Value $response
}
} catch [System.Net.WebException] {
Write-Error $_.Exception -Category ResourceUnavailable
return
} catch { # Extra catch just in case, I can't remember what might fall here
Write-Error $_.Exception -Category NotImplemented
return
}
Write-Verbose "Retrieved $($Response.ResponseUri)"
if((Test-Path variable:response) -and $response.StatusCode -eq 200) {
# Magics to figure out a file location based on the response
if($OutFile -and !(Split-Path $OutFile)) {
$OutFile = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $OutFile
}
elseif((!$Passthru -and !$OutFile) -or ($OutFile -and (Test-Path -PathType "Container" $OutFile)))
{
[string]$OutFile = ([regex]'(?i)filename=(.*)$').Match( $response.Headers["Content-Disposition"] ).Groups[1].Value
$OutFile = $OutFile.trim("\/""'")
$ofs = ""
$OutFile = [Regex]::Replace($OutFile, "[$([Regex]::Escape(""$([System.IO.Path]::GetInvalidPathChars())$([IO.Path]::AltDirectorySeparatorChar)$([IO.Path]::DirectorySeparatorChar)""))]", "_")
$ofs = " "
if(!$OutFile) {
$OutFile = $response.ResponseUri.Segments[-1]
$OutFile = $OutFile.trim("\/")
if(!$OutFile) {
$OutFile = Read-Host "Please provide a file name"
}
$OutFile = $OutFile.trim("\/")
if(!([IO.FileInfo]$OutFile).Extension) {
$OutFile = $OutFile + "." + $response.ContentType.Split(";")[0].Split("/")[1]
}
}
$OutFile = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $OutFile
}
if($Passthru) {
$encoding = [System.Text.Encoding]::GetEncoding( $response.CharacterSet )
[string]$output = ""
}
[int]$goal = $response.ContentLength
$reader = $response.GetResponseStream()
if($OutFile) {
try {
$writer = new-object System.IO.FileStream $OutFile, "Create"
} catch { # Catch just in case, lots of things could go wrong ...
Write-Error $_.Exception -Category WriteError
return
}
}
[byte[]]$buffer = new-object byte[] 4096
[int]$total = [int]$count = 0
do
{
$count = $reader.Read($buffer, 0, $buffer.Length);
Write-Verbose "Read $count"
if($OutFile) {
$writer.Write($buffer, 0, $count);
}
if($Passthru){
$output += $encoding.GetString($buffer,0,$count)
} elseif(!$quiet) {
$total += $count
if($goal -gt 0) {
Write-Progress "Downloading $Uri" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100)
} else {
Write-Progress "Downloading $Uri" "Saving $total bytes..." -id 0
}
}
} while ($count -gt 0)
$reader.Close()
if($OutFile) {
$writer.Flush()
$writer.Close()
}
if($Passthru){
$output
}
}
if(Test-Path variable:response) { $response.Close(); }
if($SessionVariable) {
Set-Variable $SessionVariable -Scope 1 -Value ([PSCustomObject]@{
Headers = ConvertTo-Dictionary -Headers $request.Headers
Cookies = $response.Cookies
UseDefaultCredentials = $request.UseDefaultCredentials
Credentials = $request.Credentials
Certificates = $request.ClientCertificates
UserAgent = $request.UserAgent
Proxy = $request.Proxy
MaximumRedirection = $request.MaximumAutomaticRedirections
})
}
if($WebSession) {
$WebSession.Cookies = $response.Cookies
}
Where am I missing a closing "}", or which other one did I mess-up with , rather than fixing it? Thanks for any help or pointers, or the working 'compiling' file.
@Jaykul is Invoke-Web v 4.2 known to be broken? Is the latest working one v 4.1? Also have you found a solution for the currently broken installation of the PoshCode module, as reported by @dhilgarth ? ( Issue #16 )
I'll take a look -- I don't think there's a broken version of Invoke-Web out there. Is there something specific you need from this? I mean, most people on 3 and 4 aren't using it because they get by with the built in Invoke-WebRequest ;-)
I'll take a look at the specific versions you linked in a minute -- but can you get by with the version that is in this project in InvokeWeb.psm1? That one is slightly handicapped, because I tried to replicate more closely the way Invoke-WebRequest works in PS3&4 (and in fact, you'll have to remove the if block which prevents it from loading when there's already a Invoke-WebRequest).
On 13 Feb 2015, at 20:34, Joel Bennett [email protected] wrote:
I'll take a look -- I don't think there's a broken version of Invoke-Web out there. Is there something specific you need from this? I mean, most people on 3 and 4 aren't using it because they get by with the built in Invoke-WebRequest ;-)
Well, I might well be missing a trick somewhere, but I am after a function that determines the filename of the download file that I give in the URL by itself. I am hoping to pass just one parameter, the Uri, and for the script to figure out the filename of the download.
And the earlier versions of your Module Get-WebFile up to ver. 3.6 from poshcode work, but just not on all web download links I am throwing at it. So I was hoping that ver. 4.1 and particularly the very latest ver. 4.2 (Id = 3913 - which appears broken would work even better for = me.)
I'll take a look at the specific versions you linked in a minute -- but can you get by with the version that is in this project in InvokeWeb.psm1 https://github.com/PoshCode/PoshCode/blob/master/InvokeWeb.psm1?
As far as I = can tell this checks if I already have Invoke-WebRequest from MS, and if I do it does not add anything. I guess this is for situations where = people want to get Invoke-WebRequest in powershell 1 or 2?
If I do (because downloading Get-Poshcode to a file = directly is NOT working either)
Get-Postcode 3913 -passthru > = Get-WebFile_4.2.ps1
and then run it
. = .\Get-WebFile_4.2.ps1
it gives me the six syntax errors I mentioned = on github poshcode #17 and on poshcode http://poshcode.org/5732 file:///Users/urs.rau/Downloads/3D%22http://poshcode.org/5732%22
C:\test_1> . .\Get-WebFile_4.2.ps1
At = C:\test_1\Get-WebFile_4.2.ps1:147 char:57
+ = [ValidateScript({if(!($Credential -or $WebSession){ throw = "ForceBasicAuth …
+ = = = ~
Unexpected token '{' in expression or = statement.
At C:\test_1\Get-WebFile_4.2.ps1:147 char:57
+ = [ValidateScript({if(!($Credential -or $WebSession){ throw = "ForceBasicAuth …
+ = = = ~
Missing closing ')' after expression in 'if' = statement.
At C:\test_1\Get-WebFile_4.2.ps1:147 char:124
+ ... meter be = set"} else { $true }})]
+ = ~~~~
Unexpected token 'else' in expression or = statement.
At C:\test_1\Get-WebFile_4.2.ps1:52 char:21
+ function = Invoke-Web {
+ = ~
Missing closing '}' in statement block.
At = C:\test_1\Get-WebFile_4.2.ps1:377 char:1
+ }}
+ ~
Unexpected = token '}' in expression or statement.
At = C:\test_1\Get-WebFile_4.2.ps1:377 char:2
+ }}
+ = ~
Unexpected token '}' in expression or statement.
= + CategoryInfo : ParserError: (:) [], = ParseException
+ FullyQualifiedErrorId : = UnexpectedToken
BTW the postcode download that does not work for me, (the reason why I use -passthru to redirect to file instead ) is, if I do:
C:\test_1> Get-PoshCode 3913 -Verbose -Debug
DEBUG: ParameterSet Name: Download
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"): Y
Set-DownloadFlag : Cannot process argument transformation on parameter 'Zone'. Cannot convert the
"<System.Security.Policy.Zone version="1">
<Zone>Internet</Zone>
</System.Security.Policy.Zone>
" value of type "System.Security.Policy.Zone" to type "System.Security.SecurityZone".
At C:\windows\system32\windowspowershell\v1.0\Modules\PoshCode\PoshCode.psm1:311 char:66
+ Get-WebFile "$($url)?dl=$id" | ConvertTo-Module | Set-DownloadFla ...
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Set-DownloadFlag], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Set-DownloadFlag
. Any idea why I might get the error:
Set-DownloadFlag : Cannot process argument transformation on parameter 'Zone'. Cannot convert the
"<System.Security.Policy.Zone version="1">
<Zone>Internet</Zone>
</System.Security.Policy.Zone>
" value of type "System.Security.Policy.Zone" to type "System.Security.SecurityZone".
Whenever I try to get the PoshCode module to download to a file? (same happens if I use -SaveAs )
Yeah, I have rewritten that download flag stuff multiple times, I can't figure out exactly why it stops working (and of course, it's completely unnecessary). You could just use the -Unblocked switch to avoid it, I think.
Posted http://poshcode.org/5736 as 4.3 because there were two parenthesis or curly braces out of place in the 4.2 post. I can't understand how that ever got pasted, but ... let's just move on.
Thanks, Jaykul, that was fast, much appreciated. Now have a working latest veriosn of invoke-web or get-webfile.
Will indeed use the -Unblocked parameter on Invoke-Web 4.3 , nice hint, thanks.
But the Get-Poshcode function I am referring to above does not have that switch as far as I can tell, so the -SaveAs and direct download on Get-Poshcode in last few lines of long issuecomment https://github.com/PoshCode/PoshCode/issues/17#issuecomment-74373212 remains broken for now.
If you're using the old PoshCode.psm1 module, you're right. The -Unblocked flag wasn't exposed and except when you -Passthru, the module explicitly calls Set-DownloadFlag which is broken (depending, I think, on the bitness of your OS and silly things like that).
There is a version of that module called Scripts.psm1 in this repository, which has a working Get-PoshCode in it. If you look at the change comments you'll see that I just stripped out all the block/unblock stuff and gave up on marking the files as being from the internet zone.
There's some "risk" in that (since the files won't be "remote" when PowerShell's ExecutionPolicy is applied), but frankly, since my interop code to set the ZONE flag has broken in each release of Windows, and Microsoft never thought the feature was important enough to expose it in the .Net framework -- screw that. PowerShell has support for block/unblock now, but I can't take take a dependency on the latest version, so that doesn't help.