#Requires -version 1.0 ## PoshCode module v 3.5 ## See comments for each function for changes ... ############################################################################################################## ## Provides functions for working with scripts from the PoshCode Repository: ## Get-PoshCodeUpgrade - get the latest version of this script from the PoshCode server ## Get-PoshCode - Search for and download code snippets ## New-PoshCode - Upload new code snippets ## Get-WebFile - Download ############################################################################################################## # Set-StrictMode -Version Latest $PoshCode = "http://PoshCode.org/" | Add-Member -type NoteProperty -Name "UserName" -Value "Anonymous" -Passthru | Add-Member -type NoteProperty -Name "ScriptLocation" -Value $($MyInvocation.MyCommand.Path) -Passthru | Add-Member -type NoteProperty -Name "ScriptVersion" -Value 3.5 -Passthru | Add-Member -type NoteProperty -Name "ApiVersion" -Value 1 -Passthru ## New-PoshCode (formerly Send-Paste) ############################################################################################################## ## Uploads code to the PowerShell Script Repository and returns the url for you. ############################################################################################################## ## Usage: ## get-content myscript.ps1 | New-PoshCode "An example for you" "This is just to show how to do it" ## would send the script with the specified title and description ## ls *.ps1 | New-PoshCode -Keep Forever ## would flood the pastebin site with all your scripts, using filename as the title ## and a generic description, and mark them for storing indefinitely ## get-history -count 5 | % { $_.CommandLine } | New-PoshCode ## would paste the last 5 commands in your history! ############################################################################################################## ## History: ## v 3.1 - Fixed the $URL parameter so that it's settable again. *This* function should work on any pastebin site ## v 3.0 - Renamed to New-PoshCode. ## Removed the -Permanent switch, since this is now exclusive to the permanent repository ## v 2.1 - Changed some defaults ## - Added "PermanentPosh" switch ( -P ) to switch the upload to the PowerShellCentral repository ## v 2.0 - works with "pastebin" (including posh.jaykul.com/p/ and PowerShellCentral.com/scripts/) ## v 1.0 - Worked with a special pastebin ############################################################################################################## function New-PoshCode { param( $Title, $Description=$(Read-Host "Please enter a description for this script"), $KeepFor="f", $Language="posh", $Author = $($PoshCode.UserName), $url= $($PoshCode) ) BEGIN { $null = [Reflection.Assembly]::LoadWithPartialName("System.Web") [string]$data = $null; [string]$meta = $null; if($language) { $meta = "format=" + [System.Web.HttpUtility]::UrlEncode($language) # $url = $url + "?" +$lang } else { $meta = "format=text" } do { switch -regex ($KeepFor) { "^d.*" { $meta += "&expiry=d" } "^m.*" { $meta += "&expiry=m" } "^f.*" { $meta += "&expiry=f" } default { $KeepFor = Read-Host "Invalid value for 'KeepFor' parameter. Please specify 'day', 'month', or 'forever'" } } } until ( $meta -like "*&expiry*" ) if($Description) { $meta += "&descrip=" + [System.Web.HttpUtility]::UrlEncode($Description) } else { $meta += "&descrip=" } $meta += "&poster=" + [System.Web.HttpUtility]::UrlEncode($Author) function Send-PoshCode ($meta, $title, $data, $url= $($PoshCode)) { $meta += "&paste=Send&posttitle=" + [System.Web.HttpUtility]::UrlEncode($Title) $data = $meta + "&code2=" + [System.Web.HttpUtility]::UrlEncode($data) $request = [System.Net.WebRequest]::Create($url) $request.ContentType = "application/x-www-form-urlencoded" $request.ContentLength = $data.Length $request.Method = "POST" $post = new-object IO.StreamWriter $request.GetRequestStream() $post.Write($data,0,$data.Length) $post.Flush() $post.Close() # $reader = new-object IO.StreamReader $request.GetResponse().GetResponseStream() ##,[Text.Encoding]::UTF8 # write-output $reader.ReadToEnd() # $reader.Close() write-output $request.GetResponse().ResponseUri.AbsoluteUri $request.Abort() } } PROCESS { switch($_) { { $_ -is [System.IO.FileInfo] } { if(!$Title) { if ($_.Extension.Length -gt 0) { $Title = $_.Name.Remove($_.Name.Length - $_.Extension.Length) } else { $Title = $_.Name } } Write-Verbose $_.FullName Write-Output $(Send-PoshCode $meta $Title $([string]::join("`n",(Get-Content $_.FullName))) $url) } { $_ -is [String] } { if(!$data -and !$Title){ $Title = Read-Host "Give us a title for your post" } $data += "`n" + $_ } ## Todo, handle folders? default { Write-Error "Unable to process $_" } } } END { if($data) { Write-Output $(Send-PoshCode $meta $Title $data $url) } } } ## Get-PoshCode (download Repository script) ############################################################################################################## ## Downloads a specified script from a Pastbin.com based site, by Id ## ### OR ### ## Searches the powershellcentral.com/script site and returns lists of results ## All search terms are automatically surrounded with wildcards ## NOTE: powershellcentral.com currently uses MySql fulltext search syntax... ############################################################################################################## ## Usage: ## Get-PoshCode 184 ## would download the original Send-Paste script to 184.ps1 ## Get-PoshCode Pastebin ## would search the repository for the original Send-Paste pastebin script ## Get-PoshCode 184 -Passthru ## would output the contents of Get-PoshCode to the pipeline ## Get-PoshCode 184 Pastebin.ps1 ## would download the original version of these scripts to Pastebin.ps1 in the current directory ## Get-PoshCode Pastebin | % { Get-PoshCode $_.Id } ## would download all the search results ... ############################################################################################################## ## History: ## v 3.2 - Added an -Upgrade switch to cause the script to get the latest version of the script. ## v 3.1 - Fixed Description and Link to work correctly on v1 ## - Add "Huddled.PoshCode.ScriptInfo" to TypeInfo, so it can be formatted by a ps1xml ## - Add ConvertTo-Module function to try to rename .ps1 scripts to .psm1 ## - Fixed exceptions thrown by searches which return no results ## - Removed the auto-wildcards!!!! ## NOTE: to get the same results as before you must now put * on the front and end of searches ## This is so that searches on the website work the same as searches here... ## My intention is to improve the website's search instead of leaving this here. ## NOTE: the website currently will not search for words less than 4 characters long ## v 3.0 - Working against the new RSS-based API ## - And using ParameterSets, which work in CTP2 ## v 2.0 - Combined with Find-Poshcode into a single script ## v 1.0 - Working against our special pastebin ############################################################################################################## function Get-PoshCode { param( [string]$query = $(throw "You must specify a query string, or the id of the paste to get"), [string]$SaveAs, [string]$Language="posh", $url = $($PoshCode), [switch]$InBrowser, [switch]$Passthru, [switch]$Upgrade ) if($Upgrade) { Get-PoshCodeUpgrade } else { write-debug "Id: $id" if(!$id) { if( !(@('text','asp','bash','cpp','csharp','posh','vbnet','xml','all') -contains $Language) ) { throw "Invalid language specified, valid values are: 'text','asp','bash','cpp','csharp','posh','vbnet','xml','all'"; } $results = @(([xml](Get-WebFile "$($url)api$($PoshCode.ApiVersion)/$($query)&lang=$($Language)" -passthru)).rss.channel.GetElementsByTagName("item")) if($results.Count -eq 0 ) { Write-Host "Zero Results for '$query'" -Fore Red -Back Black } else { $results | Select @{n="Id";e={($_.link -replace $url,'') -as [int]}}, @{n="Title";e={$_.title }}, @{n="Author";e={$_.creator }}, @{n="Date";e={$_.pubDate }}, @{n="Link";e={$_.guid.get_InnerText() }}, @{n="Description";e={"$($_.description.get_InnerText())`n" }} | ForEach { $_.PSObject.TypeNames.Insert( 0, "Huddled.PoshCode.ScriptInfo" ); $_ } } } else { if(!$InBrowser) { filter ConvertTo-Module { $oldFile = $_ if( ([IO.Path]::GetExtension($oldFile) -eq ".ps1") -and [Regex]::Match( [IO.File]::ReadAllText($oldFile), "^[^#]*Export-ModuleMember.*", "MultiLine").Success ) { $fileName = [IO.Path]::ChangeExtension($oldFile, ".psm1") Move-Item $oldFile $fileName -Force Get-Item $fileName } else { $oldFile } } if($SaveAs) { Get-WebFile "$($url)?dl=$id" -fileName $SaveAs | ConvertTo-Module } elseif($Passthru) { Get-WebFile "$($url)?dl=$id" -Passthru } else { Get-WebFile "$($url)?dl=$id" | ConvertTo-Module } } else { [Diagnostics.Process]::Start( "$($url)$id" ) } } } } ## Get-PoshCodeUpgrade ############################################################################################################## ## Downloads a new PoshCode script version, and archives old versions.. ############################################################################################################## ## History: ## v3.3 - Removes old versions ## v3.2 - First script version with Upgrade function ############################################################################################################## function Get-PoshCodeUpgrade { $VersionFile = [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ("{0}{1}" -f $PoshCode.ScriptVersion, [IO.Path]::GetExtension($PoshCode.ScriptLocation))) # Copy it to make sure we don't loose it Copy-Item $PoshCode.ScriptLocation $VersionFile # Remove old ones ... Remove-Item ( [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".*$([IO.Path]::GetExtension($PoshCode.ScriptLocation))") ) -exclude ([IO.Path]::GetFileName($VersionFile)) -Confirm # Finally, get the new one $NewFile = Get-WebFile "$($PoshCode)PoshCode.ps1" -fileName ( [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".INVALID.ps1")) if( Test-Signature $NewFile ) { Move-Item $NewFile $PoshCode.ScriptLocation -Force -passthru . $PoshCode.ScriptLocation } else { Write-Error "Signature is Not Valid on new version." Get-Item $NewFile } } ## Get-WebFile (aka wget for PowerShell) ############################################################################################################## ## Downloads a file or page from the web ## History: ## v3.8 - Add UserAgent calculation and parameter ## v3.7 - 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) ############################################################################################################## function Get-WebFile { param( $url = (Read-Host "The URL to download"), $fileName = $null, [switch]$Passthru, [switch]$quiet, [string]$UserAgent = "PoshCode/$($PoshCode.ScriptVersion)" ) Write-Verbose "Downloading '$url'" $req = [System.Net.HttpWebRequest]::Create($url); $req.UserAgent = $( "{0} (PowerShell {1}; .NET CLR {2}; {3}; http://PoshCode.org)" -f $UserAgent, $(if($Host.Version){$Host.Version}else{"1.0"}), [Environment]::Version, [Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win") ) $res = $req.GetResponse(); if($fileName -and !(Split-Path $fileName)) { $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName } elseif((!$Passthru -and ($fileName -eq $null)) -or (($fileName -ne $null) -and (Test-Path -PathType "Container" $fileName))) { [string]$fileName = ([regex]'(?i)filename=(.*)$').Match( $res.Headers["Content-Disposition"] ).Groups[1].Value $fileName = $fileName.trim("\/""'") $ofs = "" $fileName = [Regex]::Replace($fileName, "[$([Regex]::Escape(""$([System.IO.Path]::GetInvalidPathChars())$([IO.Path]::AltDirectorySeparatorChar)$([IO.Path]::DirectorySeparatorChar)""))]", "_") $ofs = " " if(!$fileName) { $fileName = $res.ResponseUri.Segments[-1] $fileName = $fileName.trim("\/") if(!$fileName) { $fileName = Read-Host "Please provide a file name" } $fileName = $fileName.trim("\/") if(!([IO.FileInfo]$fileName).Extension) { $fileName = $fileName + "." + $res.ContentType.Split(";")[0].Split("/")[1] } } $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName } if($Passthru) { $encoding = [System.Text.Encoding]::GetEncoding( $res.CharacterSet ) [string]$output = "" } if($res.StatusCode -eq 200) { [int]$goal = $res.ContentLength $reader = $res.GetResponseStream() if($fileName) { $writer = new-object System.IO.FileStream $fileName, "Create" } [byte[]]$buffer = new-object byte[] 4096 [int]$total = [int]$count = 0 do { $count = $reader.Read($buffer, 0, $buffer.Length); if($fileName) { $writer.Write($buffer, 0, $count); } if($Passthru) { $output += $encoding.GetString($buffer,0,$count) } elseif(!$quiet) { $total += $count if($goal -gt 0) { Write-Progress "Downloading $url" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100) } else { Write-Progress "Downloading $url" "Saving $total bytes..." -id 0 } } } while ($count -gt 0) $reader.Close() if($fileName) { $writer.Flush() $writer.Close() } if($Passthru){ $output } } $res.Close(); if($fileName) { Get-Item $fileName } } ## Test-Signature - Returns true if the signature is valid OR is signed by "F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5" function Test-Signature { PARAM ($ToTest) PROCESS { if($_) { $ToTest = $_ } if(!($ToTest -as [System.Management.Automation.Signature]) -and (Test-Path $ToTest -PathType Leaf)) { $ToTest = Get-AuthenticodeSignature $ToTest } if($ToTest -as [System.Management.Automation.Signature]) { $result = $false; #trap { continue } $result = ( $ToTest.Status -eq "Valid" -or (($ToTest.Status -eq "UnknownError") -and $ToTest.SignerCertificate -and ($ToTest.SignerCertificate.Thumbprint -eq "F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5") )) return $result } else { return $false } } } # SIG # Begin signature block # MIIIPgYJKoZIhvcNAQcCoIIILzCCCCsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUNQ3QyCyBaLSxMCpWVlGXuVCB # P0egggVbMIIFVzCCBD+gAwIBAgIRAO2rPg5HUjL4ofGGpnMP2jwwDQYJKoZIhvcN # AQEFBQAwgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2Fs # dCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8G # A1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNF # UkZpcnN0LU9iamVjdDAeFw0wODEwMDYwMDAwMDBaFw0wOTEwMDYyMzU5NTlaMIHE # MQswCQYDVQQGEwJVUzEOMAwGA1UEEQwFMTQ2MjMxETAPBgNVBAgMCE5ldyBZb3Jr # MRIwEAYDVQQHDAlSb2NoZXN0ZXIxFDASBgNVBAkMC01TIDA4MDEtMTdBMRowGAYD # VQQJDBExMzUwIEplZmZlcnNvbiBSZDEaMBgGA1UECgwRWGVyb3ggQ29ycG9yYXRp # b24xFDASBgNVBAsMC1NFRURVIFRvb2xzMRowGAYDVQQDDBFYZXJveCBDb3Jwb3Jh # dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK50RXT2KUvECfWZ # weqeXzTCykPPRh9nC3Hzur/mmvkQHA8iinnSKX4j19C1/MV0rAEeCU1bF7Sgxvov # ORreM1Ye/wEqJLAUP/IGZI/qsmmwasGFGbnuevpA3WieNCr5cFGl8Y5Mx6ejaDFi # O0GT9EM6gOGZaEEMRbHZc4qXT7CrWScs4Yur5bBZsowaMk5JkvZgihhnN93QolEW # ObmtQZlbBDqLuoL9fUnIexlqqIrC/4h0K8VM26HvqhgGlQF2wf4t9xCHFJiX2F7D # B10lef5aXzyPVrvxxrRWyBtCQuL7xdXneRanJaYG3B3kclc+4/6dq9a+s/huXjmE # omumgGcCAwEAAaOCAW8wggFrMB8GA1UdIwQYMBaAFNrtZHQUnBQ8q92Zqb1bKE2L # PMnYMB0GA1UdDgQWBBT5ITlG5CdiD+nI0uTqnXNGnd44QjAOBgNVHQ8BAf8EBAMC # B4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhC # AQEEBAMCBBAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwIwKzApBggrBgEFBQcC # ARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwQgYDVR0fBDswOTA3oDWg # M4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0 # LmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNv # bW9kb2NhLmNvbTAhBgNVHREEGjAYgRZKb2VsLkJlbm5ldHRAWGVyb3guY29tMA0G # CSqGSIb3DQEBBQUAA4IBAQAZxnV+BbJBohpy+wKs6U8hRiPUhDYaijzTyrZontf5 # PEyBbhAkJFIWauIaq9eSQEJeErXO/zuO6+wY/azBzOTleMM9qdGWHFtfAw5WiIuC # 90TzDBSuP7LImZV5Pb6nxRbesDF2U7EM5sBzYSWAMfpBmYRz97EHPW5QNzpBLFJn # Dhb/M27rDYh7FVjy1+C5E3glIa0A0q+lcxEtFuUij4JId+oMcfpSgYJZvR1Kvkjd # GDAtWCzvALaNFd65kChbrOqcClOCacQRnP9N4DJl/RVNKZtcUcVAyTpvOlJBA5vG # OVcsJT4TnSMjPX6d5pXMwcE1oWCUWvK99W+N81DvBBuZMYICTTCCAkkCAQEwgasw # gZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl # IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY # aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0 # LU9iamVjdAIRAO2rPg5HUjL4ofGGpnMP2jwwCQYFKw4DAhoFAKB4MBgGCisGAQQB # gjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFKUJ9y0v # Xo3ZTZ06fBb17jeqov7IMA0GCSqGSIb3DQEBAQUABIIBAAYaudEdWdDwo7K1xzdA # 7ekMKkfSCSFDLKODQfajqdFjWHfkpZilRsH9q11bHqJ8TWW+8RjaGbUcPuk0KhxY # HYcMpx161fo5xgODXBzR9AFpp1c3JiKCMenvOo/2Q7b2n2kcLX6NKsbJNrrv1QwK # KI2kfUgCG7qIiTKeLeWn+VlzgXTqrejlJsGdaxQuj3HmSWvPmvtyBXELQUjvlWDN # Zad72E6Om1JpDARGL4ajQaS4JxfoAqBTHntFmya0xGSAeGWa/6fUL62zdFw0NiqC # jD4jBi4PJJQgD95X5+QDeS0ahota2/TDvmo3lLo+oIXVRtmKMCLu+diRzgPICPu5 # 3Vk= # SIG # End signature block