Invoke-WebCommand.ps1
function Invoke-WebCommand { <# .Synopsis Invokes the commands within a web service .Description Invokes the commands within a Pipeworks web service .Example # Invoke-WebCommand is the Pipeworks function runs commands in a web service. It's automatically called in many places, but can be used on the command line to emulate and debug running on the web. .Link ConvertTo-ModuleService #> [OutputType([PSObject])] param( # The command that is being executed [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Management.Automation.CommandInfo] $Command, # A friendly name for the service [Parameter(Position=1)] [string] $FriendlyName, # The order of the displayed parameters [Parameter(Position=2)] [string[]]$ParameterOrder, # If set, allows the command to be run without any parameters. This is especially useful for Get- commands. [switch]$RunWithoutInput, # If set, these parameters will be hidden from a command handler's input and output parameters. # They will still be visible in help. [Parameter(Position=3)] [Alias('DenyParameter')] [string[]]$HideParameter, # The ID to use for Google Analytics tracking [string] $AnalyticsId, # The AdSenseID used to monetize the command with Google AdSense [string] $AdSenseID, # The AdSlotId used to monetize the command with Google AdSense [string] $AdSlot, # If set, allows the command to be downloaded [Switch]$AllowDownload, # If set, the command will be enabled as a web service. This parameter is implied by almost every other parameter (except -ShowHelp, which contravenes it), and is primarily present for backwards-compatibility. [Switch] $RunOnline, # If set, the payment has been processed by the server. This is used internally to finalize actions after payments. [switch] $PaymentProcessed, # If SessionThrottle is set, the request handler will wait for at least the -SessionThrottle before # allowing the user to re-run the command. This can be useful in mitigating Denial of Service attacks # as well as providing an avenue to upsell (i.e. a free user can run a command once a minute, where as a premium user can run requests without the throttle) [Timespan] $SessionThrottle = "0:0:0", # If set, escapes the output from the command, so it can be embedded into a webpage [switch]$EscapeOutput, # The CSS Style section to use for the page [Hashtable]$Style, # If set, will output the results of the command without encasing it in a container [Switch]$PlainOutput, # If set, will output the results of the command with a particular content type [string]$ContentType, # Sets the method used for the web form. By default, POST is used, but Get is required if the command outputs binary data (like image streams) [ValidateSet('POST', 'GET')] [string]$Method = "POST", # If set, will not add sharing links to the page [Switch]$AntiSocial, # The Module Service URL. # If this is not set, this will automatically be the url to the command directory. [Uri]$ServiceUrl, # The Web Front End for a command, declared in HTML. Setting a Web Front End will override the default front end with any web page. [String]$WebFrontEnd, # The Mobile Web Front End for a command, declared in HTML. Setting a Mobile Web Front End will override the default front end with any web page. [String]$MobileWebFrontEnd, # A table of parameters that will get their value from a cookie [Hashtable]$CookieParameter = @{}, # If set, will save the output in a cookie. [string]$SaveInCookie, # A table of parameter URL aliases. These allow URLs to the sevice to become shorter. [Hashtable]$ParameterAlias= @{}, # Default values for the parameters [Alias('DefaultParameter')] [Hashtable]$ParameterDefaultValue = @{}, # Parameters that are taken from the web.config settings [Alias('SettingParameter')] [Hashtable]$ParameterFromSetting= @{}, # Parameters that are taken from the user settings [Alias('UserParameter')] [Hashtable]$ParameterFromUser= @{}, # Any additional commands that the command can be piped into. # In the Sandbox service, these commands will be allowed as well [string[]]$PipeInto, # If set, will allow the command to be run in a sandbox [Switch]$RunInSandBox, # The margin on either side of the module content. Defaults to 7.5%. [ValidateRange(0,100)] [Double] $MarginPercent = 7.5, # The margin on the left side of the module content. Defaults to 7.5%. [ValidateRange(0,100)] [Double] $MarginPercentLeft = 7.5, # The margin on the left side of the module content. Defaults to 7.5%. [ValidateRange(0,100)] [Double] $MarginPercentRight = 7.5, # If set, will only allow logged-in users to run the command [Alias('RequiresLogin', 'RequiredLogin', 'LoginRequired')] [Switch] $RequireLogin, # If set, will require an app key to run the command. Logged in users will automatically use their app key [Alias('RequiresAppKey', 'RequiredAppKey', 'AppKeyRequired')] [Switch] $RequireAppKey, # If set, will track uses of an appkey. [string] $UserTable, # The partition in a usertable containing data [string] $UserPartition, # If set, will track parameters the user supplied for a command, and when they ran it. [switch] $KeepUserHistory, # If set, will keep a history of command input and output [switch] $KeepHistory, # If set, will keep results in the UseTrackingTable [switch] $KeepResult, # If set, will track uses of the command. [string] $UseTrackingTable, # If set, will track unique properties of the output, such as the number of times an item with a particular ID is output. [string[]] $TrackProperty, # If set, will track unique parameters in the input, such as the number of times an input value is used [string[]] $TrackParameter, # The name of the web.config setting containing the storage account name. Required for tracking. [string] $StorageAccountSetting = 'AzureStorageAccountName', # The name of the web.config setting containing the storage account key. Required for tracking. [string] $StorageKeySetting = 'AzureStorageAccountKey', # Allows the command to be run only if the user is logged as one of the values. # # # Values can be email addresses or userids. With email addresses, wildcards (i.e. *@microsoft.com) will be supported. [string[]] $IfLoggedInAs, # Validates that users are allowed to run the command by checking objects with a Condition property found in ValidUserPartition [string] $ValidUserPartition, # Any config settings [Hashtable]$ConfigSetting, # A table of costs per run by locale [Hashtable] $CostPerRun, # If set, makes the person pay before the command is actually executed [Alias('BuyNow')] [Switch] $PayNow, # If set, makes the person subscribe before the command is actually executed [Switch] $SubscribeNow, # The currency used for the transaction. By default, US Dollars. Additional currencies are treated as additional purchasing options [string[]]$Currency = "USD", # The frequency for the subscription. By default, every month. Additional billing frequencies are used to create additional subscription options. [ValidateSet("Daily", "Weekly", "Monthly", "Yearly")] [string[]] $BillingFrequency = 'Monthly', # If set, prompts for confirmation before a command will be run [Switch] $PromptForConfirmation, # A custom confirmation message. If provided, -PromptForConfirmation is assumed. [string] $ConfirmationMessage, # A location to redirect to when the command is complete. [Uri] $RedirectTo, # The amount of time to wait before redirecting (by default, no time) [Timespan] $RedirectIn, # The cost to run the command. Providing more than one cost is used to create additional subscription options. [Double[]] $Cost, # A cost factor table [Hashtable] $CostFactor= @{}, # If set, will show the help for a command, but will not run it [Switch] $ShowHelp, # If provided, will use this PSOBject for the command help. [PSObject] $HelpObject, # If set, will hide the related items in command help. [Switch] [Alias('HideRelatedItems')] $HideRelatedItem, # The string shown to a person when the command does not throw an error but has not output [Alias('NoOp')] [String] $NoOutputMessage, # The properties from the output to display [String[]] $OutputProperty, # Will link selected items to items from a command [Hashtable] $LinkTo, # If set, the command will be hidden from web front ends, but can still be directly invoked. [Switch] $Hidden, # If provided, the output will be rendered with a given type name. # Changing this will allow you to override the look and feel of any object. [string] $AsTypename, # If provided, will lookup the user by email and will run commands as them. # This is used for montezation, and should only be used directly very carefully. [string] $AsEmail, # If provided, will lookup the user by email and will run commands as them. # This is used for montezation, and should only be used directly very carefully. [string] $AsUserId, # If provided, will not use ajax while running the command [Switch] $NoAjax, # If provided, will use window.external.notify to provide a notification message when complete. [string] $Notify, # If set, default values and other information the pipeworks manifest will be ignored. This allows you to execute commands with parameters that would not be available to a web user. [Switch] $IgnoreManifest ) begin { if (-not $global:session) { $global:Session = @{} } if (-not $global:Application) { $global:Application= @{} } $ComputeTotalCost = { $totalCost = 0 $listOfCosts= @() $listOfFrequencies= @() $listOfCurrencies = @() for ($i = 0; $i -lt $cmdOptions.Cost.Count; $i++) { $listOfCosts+= $cmdOptions.Cost[$i] } if ($cmdOptions.CostFactor) { $factoredCost = 0 foreach ($kv in $cmdOptions.CostFactor.getEnumerator()) { $parameterValue = $mergedParameters["$($kv.Value.Parameter)"] if ($kv.Value.CostMap) { if ($kv.Value.CostMap[$parameterValue]) { $factoredCost += $kv.Value.CostMap[$parameterValue] } elseif ($kv.Value.CostMap[$parameterValue -as [int]]) { $factoredCost += $kv.Value.CostMap[$parameterValue -as [int]] } } elseif ($kv.Value.CostPerItem) { $factoredCost += $kv.Value.CostPerItem * $parameterValue } } $listOfCosts += $factoredCost } $confirmLink = "$fullUrl" if ($confirmLink.Contains("?")) { $confirmLink += "&" } else { $confirmLink = $confirmLink.TrimEnd("/") + "/?" } $costString = "$" + $totalCost } $getProxyCommand = { if (-not $script:CachedProxies) { $script:CachedProxies = @{} } if ($script:CachedProxies["$command"]) { $proxyCommandText = $script:CachedProxies["$command"] } $proxyCommandMetaData = [Management.Automation.CommandMetaData]$command # then strip off hidden parameters foreach ($p in $hideParameter){ $null = $proxyCommandMetaData.Parameters.Remove($p) } $paramBlock = [Management.Automation.ProxyCommand]::GetParamBlock($proxyCommandMetaData) $remoteCommandUrl= $serviceUrl.ToString() + "?command=$command" $handleResponseScript = " if (`$getWebCommandLink) { return `$str } `$xmlResult = `$str -as [xml] if (`$xmlResult) { Write-Verbose 'Response is XML' if (`$str -like '*<Object*') { Write-Verbose 'Response is Object Xml' Write-Verbose `$str `$str | Select-Xml //Object | ForEach-Object { `$_.Node } | ForEach-Object { if (`$_.Type -like 'System.*' -and `$_.'#text') { `$_.'#text' } elseif (`$_.Property) { `$_ | Select-Object -ExpandProperty Property | ForEach-Object -Begin { `$outObject = @{} } { `$name = `$_.Name `$value = `$_.'#Text' `$outObject[`$name] = `$value } -End { New-Object PSObject -Property `$outObject } } } } else { Write-Verbose 'Response is Normal Xml' `$strWrite = New-Object IO.StringWriter `$xmlResult.Save(`$strWrite) `$strOut = `"`$strWrite`" `$strOut.Substring(`$strOut.IndexOf([environment]::NewLine) + 2) } } elseif (`$str -and (`$str -notlike '*://*' -or (`$str.ToCharArray()[1..100] | ? { `$_ -eq ' '} ))) { Write-Verbose 'Response is Text with spaces' `$str.ResponseText } elseif (`$xmlHttp.ResponseBody) { Write-Verbose 'Response is Data' `$r } elseif (`$xmlHttp.ResponseText) { Write-Verbose 'Response is Text' `$str } else { Write-Verbose 'Unknown Response' } " $script:CachedProxies["$command"] = $proxyCommandText = " function $($command.Name) { $([Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($proxyCommandMetaData)) param( $paramBlock, [Timespan]`$WebCommandTimeout = '0:0:45', $(if ($CmdOptions.RequireAppKey -or $cmdOptions.RequireLogin -or $cmdOptions.IfLoggedInAs -or $cmdOptions.ValidUserPartition) { " # An API Key [string]`$AppKey, # An API Key [string]`$AppKeySetting, " }) [switch]`$GetWebCommandLink ) begin { Add-Type -AssemblyName System.Web `$xmlHttp = New-Object -ComObject Microsoft.XmlHttp `$remoteCommandUrl = '$RemoteCommandUrl' `$wc = New-Object Net.Webclient } process { if (`$psBoundParameters.AppKey) { `$script:CachedAppKey = `$appKey } elseif (`$psBoundParameters.AppKeySetting) { `$script:CachedAppKey = Get-SecureSetting `$psBoundParameters.AppKeySetting -ValueOnly } if (`$script:CachedAppKey) { `$appKey = `$script:CachedAppKey } else { `$appKey = '' } `$result = `$null `$nvc = New-Object Collections.Specialized.NameValueCollection `$urlParts = foreach (`$param in `$psBoundParameters.Keys) { if ('WebCommandTimeout', 'GetWebCommandLink', 'AppKey', 'AppKeySetting' -contains `$param) { continue } `$null = `$nvc.Add(`"$($command.Name)_`$param`", `"`$(`$psBoundParameters[`$param])`") `"$($command.Name)_`$param=`$([Web.HttpUtility]::UrlEncode(`$psBoundParameters[`$param]))`" } `$urlParts = `$urlParts -join '&' if (`$remoteCommandUrl.Contains('?')) { `$fullUrl = `$RemoteCommandUrl + '&' + `$urlParts } else { `$fullUrl = `$RemoteCommandUrl + '?' + `$urlParts } if (`$GetWebCommandLink) { `$fullUrl += '&-GetLink=true' `$null = `$nvc.Add('GetLink', 'true') } else { $(if (-not $cmdOptions.PlainOutput) { "`$fullUrl += '&AsXml=true'" }) `$null = `$nvc.Add('AsXml', 'true') } if (`$AppKey) { if (`$fullUrl.Contains('?')) { `$fullUrl += '&AppKey=' + `$appKey } else { `$fullUrl += '?AppKey=' + `$appKey } } `$sendTime = Get-Date Write-Verbose `"`$fullUrl - Sent `$sendTime`" `$r = `$wc.UploadValues(`"`$remoteCommandUrl`", `"POST`", `$nvc) if (-not `$?) { return } `$str = [Text.Encoding]::UTF8.GetString(`$r) Write-Verbose `"`$fullUrl - Response Received `$(Get-Date)`" $handleResponseScript } } " } } process { $commandWasRun = $false $inputWasRequested = $false $commandError = $null if (-not $ServiceUrl) { $ServiceUrl = "/" } # if (-not $request -and $Response) { # Write-Error "Must be run within a web site" # return # } $cmdmd = $Command -as [Management.Automation.CommandMetaData] $cmdOptions = @{} + $psBoundParameters if (-not $FriendlyName) { $FriendlyName = $Command.Name } $manifestIsTemporary = $false if (-not $pipeworksManifest) { if ($Command.Module) { $global:pipeworksManifest = Get-PipeworksManifest -ModuleName $Command.Module if (-not ($request -and $response)) { $manifestIsTemporary = $true } } } if (-not $IgnoreManifest) { if ($pipeworksManifest.WebCommand.($Command.Name)) { foreach ($kv in $pipeworksManifest.WebCommand.($command.Name).GetEnumerator()) { $cmdOptions[$kv.Key] = $kv.Value Set-Variable -Name $kv.Key -Value $kv.Value } } } $RequestParameterNames = @{} foreach ($k in @($request.Params.Keys)) { if (-not $k) { continue } $RequestParameterNames[$k] = $request[$k] } if ($storageAccountSetting) { $storageAccount = Get-SecureSetting -Name $storageAccountSetting -ValueOnly } if ($storageKeySetting) { $storageKey = Get-SecureSetting -Name $storageKeySetting -ValueOnly } $cmdHasErrors = $null if ($AsEmail -or $AsUserId) { $userExists = $null if ($pipeworksManifest.UserDB) { $TheUserDB = @{} + $pipeworksManifest.UserDB if ($AsEmail) { $userExists = Select-SQL -FromTable $pipeworksManifest.UserDB.Name -ConnectionStringOrSetting $pipeworksManifest.UserDB.ConnectionSetting -Where "UserEmail = '$($AsEmail)'" } elseif ($AsUserId) { $userExists = Select-SQL -FromTable $pipeworksManifest.UserDB.Name -ConnectionStringOrSetting $pipeworksManifest.UserDB.ConnectionSetting -Where "UserID = '$($AsUserId)'" } $null = $TheUserDB.Remove("Name") } elseif ($pipeworksManifest.UserTable) { $TheUserTable = @{} + $pipeworksManifest.UserTable if (-not $cmdOptions.UserTable) { $cmdOptions.UserTable = $TheUserTable.Name $UserTable = $TheUserTable.Name } if (-not $cmdOptions.UserPartition) { $cmdOptions.UserPartition = $TheUserTable.Partition $UserPartition = $TheUserTable.Partition } if (-not $cmdOptions.StorageAccountSetting) { $storageAccountSetting = $cmdOptions.StorageAccountSetting = $TheUserTable.StorageAccountSetting $storageAccount = Get-SecureSetting -Name $storageAccountSetting -ValueOnly } if (-not $cmdOptions.StorageKeySetting) { $storageKeySetting= $cmdOptions.StorageKeySetting = $TheUserTable.StorageKeySetting $storageKey= Get-SecureSetting -Name $storageKeySetting -ValueOnly } $TheUserTable.Remove("Name") $TheUserTable.UserTable = $pipeworksManifest.UserTable.Name if ($AsEmail) { $userExists = Search-AzureTable -TableName $cmdOptions.UserTable -Filter "PartitionKey eq '$($cmdOptions.UserPartition)' and UserEmail eq '$($AsEmail)'" -StorageAccount $storageAccount -StorageKey $storageKey } elseif ($AsUserId) { $userExists = Search-AzureTable -TableName $cmdOptions.UserTable -Filter "PartitionKey eq '$($cmdOptions.UserPartition)' and RowKey eq '$($AsUserId)'" -StorageAccount $storageAccount -StorageKey $storageKey } } if (-not $userExists) { if ($AsEmail) { $userExists = New-Object PSObject -Property @{ UserEmail = $AsEmail } } elseif ($AsUserId) { $userdomain = if ($pipeworksManifest.DomainSchematics) { @($pipeworksManifest.DomainSchematics.Keys)[0] } else { "unknown.com" } $userExists = New-Object PSObject -Property @{ UserEmail = "$asUserId@$userdomain" } } } if ($AsEmail) { if ($pipeworksManifest.UserDB) { New-Object PSObject -Property $TheUserDB | Confirm-Person -WebsiteUrl $serviceUrl -Email $userExists.UserEmail -PersonObject $userExists } else { $confirmedUserAccount = New-Object PSObject -Property $TheUserTable | Confirm-Person -WebsiteUrl $serviceUrl -Email $userExists.UserEmail -PersonObject $userExists -StorageAccountSetting $StorageAccountSetting -StorageKeySetting $StorageKeySetting } } elseif ($AsUserId -and $userExists) { $confirmedUserAccount = $userExists } if ($confirmedUserAccount) { $session["User"] = $confirmedUserAccount } } if ($session -and (-not $session['User'])) { if ($cmdOptions.IfLoggedInAs -or $cmdOptions.RequireLogin -or $cmdOptions.ValidUserPartition) { $confirmHtml = . Confirm-Person -WebsiteUrl $ServiceUrl if (-not $session['User']) { '<span style="margin-top:3%;margin-bottom:3%" class=''ui-state-error''>You have to log in</span><br/>' + $confirmHtml return } } } if ($cmdOptions.IfLoggedInAs -or $cmdOptions.ValidUserPartition) { $ok = Confirm-Person -CheckId -WebsiteUrl $ServiceUrl -IfLoggedInAs $cmdOptions.IfLoggedInAs -ValidUserPartition $cmdOptions.ValidUserPartition if (-not $ok) { return } } <# $okIf = @() # ValidateUserTable preceeds IfLoggedInAs, and $ok is set first. # this way, logins in tables work as well as specific whitelists if ($cmdOptions.ValidUserPartition) { $okUserList = Search-AzureTable -TableName $UserTable -Filter "PartitionKey eq '$($cmdOptions.ValidUserPartition)' and IfLoggedInAs ne ''" -Select IfLoggedInAs -StorageAccount $storageAccount -StorageKey $storageKey -ExcludeTableInfo $okUserIf = $okUserList | Select-Object -ExpandProperty IfLoggedInAs $okIf += $okUserIf } if ($cmdOptions.IfLoggedInAs) { $okIf += $cmdOptions.IfLoggedInAs } if ($okIf) { $ok = $false $global:notOkReason = "" foreach ($if in $okIf) { if ($If -eq '*') { if ($session['User']) { $ok= $true break } $global:notOkReason = "Not Logged In" } if ($if -like "*@*") { if ($session["User"].UserEmail -like $if) { $ok = $true break } } elseif ($if -like "*:*") { $ifParts = $if -split ':' if ($ifParts[0] -ieq 'memberOf') { if ($session['User'].MemberOf -and $session['User'].MemberOf -like "*$($ifParts[1])*") { $ok = $true break } $global:notOkReason = "Not Member Of $($ifParts[1])" } elseif ($ifParts[0] -ieq 'notMemberOf') { if ($session['User'].MemberOf -and $session['User'].MemberOf -notlike "*$($ifParts[1])*") { $ok = $true break } $global:notOkReason = "Member Of $($ifParts[1])" } elseif ($ifParts[0] -ieq 'awarded') { if ($session['User'].Awards -and $session['User'].Awards -like "*$($ifParts[1])*") { $ok = $true break } } elseif ($ifParts[0] -ieq 'notAwarded') { if ($session['User'].Awards -and $session['User'].Awards -notlike "*$($ifParts[1])*") { $ok = $true break } } elseif ($ifParts[0] -ieq 'interactionCount') { $interactionCountOperations = if ($ifParts[1] -like "*>=*") { if ($session['User'].InteractionCount) { $interactionCountData = ConvertFrom-StringData -StringData "$($session['User'].InteractionCount)".Replace(':', '=') -ErrorAction SilentlyContinue $equation = $ifParts[1] -split "\>=" if (($interactionCountData[$equation[0].Trim()] -as [double]) -ge ($equation[1] -as [double])) { $ok=$true break } } } elseif ($ifParts[1] -like "*>*") { if ($session['User'].InteractionCount) { $interactionCountData = ConvertFrom-StringData -StringData "$($session['User'].InteractionCount)".Replace(':', '=') -ErrorAction SilentlyContinue $equation = $ifParts[1] -split "\>" if (($interactionCountData[$equation[0].Trim()] -as [double]) -gt ($equation[1] -as [double])) { $ok=$true break } } } if ($ifParts[1] -like "*<=*") { if ($session['User'].InteractionCount) { $interactionCountData = ConvertFrom-StringData -StringData "$($session['User'].InteractionCount)".Replace(':', '=') -ErrorAction SilentlyContinue $equation = $ifParts[1] -split "\<=" if (($interactionCountData[$equation[0].Trim()] -as [double]) -le ($equation[1] -as [double])) { $ok=$true break } } } elseif ($ifParts[1] -like "*<*") { if ($session['User'].InteractionCount) { $interactionCountData = ConvertFrom-StringData -StringData "$($session['User'].InteractionCount)".Replace(':', '=') -ErrorAction SilentlyContinue $equation = $ifParts[1] -split "\<" if (($interactionCountData[$equation[0].Trim()] -as [double]) -lt ($equation[1] -as [double])) { $ok=$true break } } } } } else { if ($session["User"].UserId -eq $if) { $ok = $true break } $global:notOkReason = "Not Correct UserID" } } if (-not $ok) { return } } #> $depth = 0 $fullUrl = "$($request.Url)" if ($request -and $request.Params -and $request.Params["HTTP_X_ORIGINAL_URL"]) { #region Determine the Relative Path, Full URL, and Depth $originalUrl = $context.Request.ServerVariables["HTTP_X_ORIGINAL_URL"] $urlString = $request.Url.ToString().TrimEnd("/") $pathInfoUrl = $urlString.Substring(0, $urlString.LastIndexOf("/")) $protocol = ($request['Server_Protocol'].Split("/", [StringSplitOptions]"RemoveEmptyEntries"))[0] $serverName= $request['Server_Name'] $port= $request.Url.Port $fullOriginalUrl = if (($Protocol -eq 'http' -and $port -eq 80) -or ($Protocol -eq 'https' -and $port -eq 443)) { $protocol+ "://" + $serverName + $originalUrl } else { $protocol+ "://" + $serverName + ':' + $port + $originalUrl } $rindex = $fullOriginalUrl.IndexOf($pathInfoUrl, [StringComparison]"InvariantCultureIgnoreCase") $relativeUrl = $fullOriginalUrl.Substring(($rindex + $pathInfoUrl.Length)) if ($relativeUrl -like "*/*") { $depth = @($relativeUrl -split "/" -ne "").Count - 1 if ($fullOriginalUrl.EndsWith("/")) { $depth++ } } else { $depth = 0 } #endregion Determine the Relative Path, Full URL, and Depth $fullUrl = $fullOriginalUrl } $fullUrl = $fullUrl -ireplace "\?widget=true", "" -ireplace "\?inline=true" $global:FullUrl = $fullUrl $depthChunk = "../" * $depth $commandParameters = Get-WebInput -ParameterAlias $cmdOptions.ParameterAlias -CommandMetaData $command -DenyParameter $HideParameter -ErrorAction SilentlyContinue -ErrorVariable ValidationErrors # Prefer supplied parameters to default values, but remove one or the other or the commands will not run $defaultValues = $cmdOptions.parameterDefaultValue $cmdParamNames = $commandParameters.Keys foreach ($k in @($defaultValues.Keys)) { if (-not $k) { continue} if ($cmdParamNames -contains $k -and $commandParameters[$k]) { $null = $defaultValues.Remove($k) } } if (-not $defaultValues) { $defaultValues = @{} } if (-not $commandParameters ) { $commandParameters = @{} } $parametersFromCookies = @{} if ($cmdOptions.CookieParameter) { foreach ($parameterCookieInfo in $cmdOptions.CookieParameter.GetEnumerator()) { if ($command.Parameters[$parameterCookieInfo.Key]) { $cookie = $request.Cookies[$parameterCookieInfo.Value] if ($cookie) { $parametersFromCookies[$parameterCookieInfo.Key] = $cookie } } } foreach ($k in @($parametersFromCookies.Keys)) { if ($cmdParamNames -contains $k -and $commandParameters[$k]) { $null = $parametersFromCookies.Remove($k) } } } $parametersFromSettings = @{} if ($cmdOptions.ParameterFromSetting) { foreach ($parameterSettingInfo in $cmdOptions.parameterFromSetting.GetEnumerator()) { if ($command.Parameters[$parameterSettingInfo.Key]) { $webConfsetting = Get-SecureSetting -Name $parameterSettingInfo.Value -ValueOnly if ($webConfsetting ) { $parametersFromSettings[$parameterSettingInfo.Key] = $webConfsetting } } } foreach ($k in @($parametersFromSettings.Keys)) { if ($cmdParamNames -contains $k -and $commandParameters[$k]) { $null = $parametersFromSettings.Remove($k) } } } $parametersFromUser = @{} if ($cmdOptions.ParametersFromUser) { foreach ($parameterUserInfo in $cmdOptions.parametersFromUser.GetEnumerator()) { if ($command.Parameters[$parameterUserInfo.Key]) { $userSetting = if ($session -and $session['User'].($parameterUserInfo.Key)) { $session['User'].($parameterUserInfo.Key) } else { $null } if ($userSetting ) { $parametersFromUser[$parameterUserInfo.Key] = $userSetting } } } foreach ($k in @($parametersFromUser.Keys)) { if ($cmdParamNames -contains $k -and $commandParameters[$k]) { $null = $parametersFromUser.Remove($k) } } } $nonDefaultParameters = @{} + $commandParameters + $parametersFromCookies + $parametersFromSettings + $parametersFromUser $mergedParameters = @{} + $commandParameters + $defaultValues + $parametersFromCookies + $parametersFromSettings + $parametersFromUser if (-not $script:CachedHelp) { $script:CachedHelp = @{} } if (-not $script:CachedHelp["$Command"]) { $script:CachedHelp["$Command"] = Get-Help -Name "$Command" } $help = $helpObj = $script:CachedHelp["$Command"] if (-not $psBoundParameters.HelpObject) { $HelpObject = $helpObj } if ($RequestParameterNames.'Download') { # If the module can be downloaded, let the command be downloaded. Otherwise, download the proxy if ($cmdOptions.AllowDownload) { $proxyCommandText = "function $($Command.Name) { $($Command.Definition) } " } elseif (-not $ShowHelp) { $proxyCommandText = . $getProxyCommand } else { $proxyCommandText = " " } if ($Request.Params["AsHtml"]) { $webPage = Write-ScriptHtml ([ScriptBlock]::create($proxyCommandText)) $webPage } else { #$proxyCommandText $response.ContentType = "text/plain" $response.Write($proxyCommandText) $response.Flush() } return } elseif ($ShowHelp -or ($Request -and $Request["GetHelp"]) -or $FullUrl.EndsWith("-?")){ # The Help Handler if ($HelpObject) { $commandTitleArea = "<h2> $($HelpObject.Details.Name) $(if (($AllowDownload -or $pipeworksManifest.AllowDownload) -and $Command.ScriptBlock.File) { "<span style='font-size:.33em'><a href='?Download=true&AsHtml=true'>(View Source)</a></span>" } else { '' }) </h2>" } else { $commandTitleArea = "<h2> $command $(if (($AllowDownload -or $pipeworksManifest.AllowDownload) -and $Command.ScriptBlock.File) { "<span style='font-size:.33em'><a href='?Download=true&AsHtml=true'>(View Source)</a></span>" } else { '' }) </h2>" } $helpObj = $HelpObject if (-not $script:CachedHelpContent) { $script:CachedHelpContent = @{} } if (-not $script:CachedHelpContent."$Command") { $script:CachedHelpContent."$command" = if ($HelpObj.Synopsis) { $description = @($helpObj.Description)[0].Text "<h3>$($helpObj.Synopsis)</h3>" + (ConvertFrom-Markdown -Markdown "$description " ) + "<BR/>" $navLinks = @(@($helpObj.relatedLinks)[0].NavigationLink) $examples = @(@($helpObj.examples)[0].Example) $parameters = @(@($helpObj.parameters)[0].Parameter) $seeAlso = foreach ($nav in $navLinks) { if ($nav.LinkText) { # Link to topic or command $isCommand = if ($command.ScriptBlock.Module.ExportedFunctions.($nav.LinkText) -or $command.ScriptBlock.Module.ExportedCmdlets.($nav.LinkText)) { $command.ScriptBlock.Module.ExportedCommands[$nav.LinkText] } else { $false } if ($isCommand) { Write-Link -Style @{ "font-size"="small"; "text-align" = "center" "padding" = "5px" width="66%" } -Url "$($depthChunk)$($nav.LinkText)/" -Caption $nav.Linktext -CssClass btn } elseif ($nav.LinkText -like "*_*") { Write-Link -Style @{ "font-size"="small"; "text-align" = "center" "padding" = "5px" width="66%" } -Url "$($depthChunk)$($nav.LinkText.Replace('_', ' '))" -Caption $nav.LInktext.Replace("_"," ") -CssClass btn } } elseif ($nav.Uri) { # External link Write-Link -Style @{ "font-size"="small"; "text-align" = "center" "padding" = "5px" "margin-top" = "3%" width="66%" } -Url $nav.Uri -Caption $nav.Uri -CssClass btn } } $examples = foreach ($ex in $examples) { if ($ex) { $s = $ex | Out-String $s = $s.Substring($s.IndexOf("C:\PS>") + 6) # One line trimmed example, use remarks if ($s.Trim().Length -ge 63 -and $s.Trim().Length -lt 64) { $remarks = $s.Remarks | ForEach-Object {$_.Text } $s = $remarks } $sAsScriptBlock = try { [ScriptBlock]::Create($s) } catch { } if ($sAsScriptBlock) { Write-ScriptHTML -Text $sAsScriptBlock } else { $s } } } $parameterLayer = @{} foreach ($param in $parameterS) { if (-not $param) { continue } if ($HideParameter -contains $param.Name) { continue } $parameterLayer[$param.Name] = ConvertFrom-Markdown -Markdown "$(@($param.Description)[0].Text) " -ScriptAsPowerShell } $helpTabs = @{} if ($examples) { $exLayer = @{} $c = 0 foreach ($ex in $examples) { $c++ $exLayer["Example $c"] = $ex } $helpTabs.Examples= New-Region -Style @{ "Font-size" = "small" } -AsNewspaper -NewspaperColumn 1 -ExpandNewspaperColumn $exLayer.Keys -layer $exLayer -layerId "$($command)_Example_Content" } if ($seeAlso -and (-not $HideRelatedItem)) { $helpTabs."Related Links" = "<p style='text-align:center'>" + ($seeAlso -join "<BR style='line-height:250%' />") + "</p>" } if ($parameterLayer) { $helpTabs.Parameters = New-Region -Style @{ "Font-size" = "small" } -AsNewspaper -ExpandNewspaperColumn $parameterLayer.Keys -LayerId "$($Command)_Parameters" -layer $parameterLayer -NewspaperColumn 1 } $order = if ($helpTabs."Related Links") { 'Related Links', 'Parameters', 'Examples' } else { 'Examples', 'Parameters' } New-Region -LayerID "$($command)_HelpContent" -Style @{ "Font-size" = "Medium" } -AsNewspaper $helpTabs -Order $order -NewspaperColumn .6, .4, 1 -NewspaperHeadingSize 2 } else { # Syntax only help, just display a pre "<pre>$($helpObj)</pre>" } } $helpContent = $script:CachedHelpContent."$command" ($commandTitleArea, ($helpContent -join '<br style="clear:both" />')| New-Region -Style @{ }) -join ([Environment]::NewLine) return } elseif ($Request -and $Request["Platform"]) { $platform = $request["Platform"] $allHiddenParameters =@() + $HideParameter if ($ParameterDefaultValue) { $allHiddenParameters += $ParameterDefaultValue.Keys } $inputForPlatform = Request-CommandInput -Platform $platform -CommandMetaData $cmdMd -DenyParameter $allHiddenParameters if ($platform -ne 'Web' -and ($inputForPlatform -as [xml])) { $strWrite = New-Object IO.StringWriter ([xml]$inputForPlatform).Save($strWrite) $resultToOutput = "$strWrite" -replace "encoding=`"utf-16`"", "encoding=`"utf-8`"" if (-not $cmdOptions.ContentType) { $response.ContentType ="text/xml" } $response.Write("$resultToOutput") } else { if ($platform -ne 'Web') { $Response.ContentType = 'text/plain' } else { $inputForPlatform = $inputForPlatform | New-WebPage -UseJQueryUI } $Response.Write($inputForPlatform) } } elseif ($request -and $Request["Body"] -and $Request["From"] -and $Request["To"] -and $Request["SmsSid"] -and $Request["AccountSid"]) { # Text handler # The command is being texted. If the command contains parameters that match, use them, otherwise, pass the whole # body as an embedded script in data language mode. $cmdParams = @{} + $commandParameters if ($cmdOptions.ShowHelp) { # Text Back the Description $helpObj= (Get-Help $cmdMd.Name) $description = if ($helpObj.Description) { $helpObj.Description[0].text.Replace('"',"'").Replace('<', '<').Replace('>', '>').Replace('$', '`$') } else { "" } $response.contentType = 'text/xml' $response.Write("<?xml version='1.0' encoding='UTF-8'?> <Response> <Sms>$($cmdMd.name): $([Security.SecurityElement]::Escape($Description))</Sms> </Response> ") $response.Flush() return } #region Conditionally move any parameters found into the cmdparams if ($command.Parameters.From -and $request['From']) { $cmdParams["From"] = $request['From'] } if ($command.Parameters.To -and $request['To']) { $cmdParams["To"] = $request['To'] } if ($command.Parameters.Body -and $request['Body']) { $cmdParams["Body"] = $request['Body'] } if ($command.Parameters.AccountSid -and $request['AccountSid']) { $cmdParams["Accountsid"] = $request['Accountsid'] } if ($command.Parameters.SmsSid -and $request['SmsSid']) { $cmdParams["SmsSid"] = $request['SmsSid'] } if ($command.Parameters.FromCity -and $request['FromCity']) { $cmdParams["FromCity"] = $request['FromCity'] } if ($command.Parameters.FromState -and $request['FromState']) { $cmdParams["FromState"] = $request['FromState'] } if ($command.Parameters.FromZip -and $request['FromZip']) { $cmdParams["FromZip"] = $request['FromZip'] } if ($command.Parameters.FromCountry -and $request['FromCountry']) { $cmdParams["FromCountry"] = $request['FromCountry'] } if ($command.Parameters.ToCity -and $request['ToCity']) { $cmdParams["ToCity"] = $request['ToCity'] } if ($command.Parameters.ToState -and $request['ToState']) { $cmdParams["ToState"] = $request['ToState'] } if ($command.Parameters.ToZip -and $request['ToZip']) { $cmdParams["ToZip"] = $request['ToZip'] } if ($command.Parameters.ToCountry -and $request['ToCountry']) { $cmdParams["ToCountry"] = $request['ToCountry'] } $allowedParameter = @($cmdMd.Parameters.Keys ) if (-not $AllowedParameter) { $allowedParameter = $cmdMd.Parameters.Keys } # If a parameter set was provided, filter out parameters from other parameter sets if ($parameterSet) { $allParameters = $allowedParameter | Where-Object { $cmdMd.Parameters[$_].Attributes | Where-Object { $_.ParameterSetName -eq $parameterSet } } } if ($parameterSet) { $allParameters = $allParameters | Where-Object { $cmdMd.Parameters[$_].Attributes | Where-Object { $_.ParameterSetName -eq $parameterSet } } } $allParameters = foreach ($param in $allowedParameter) { if ($HideParameter -notcontains $param) { $param } } # Order parameters if they are not explicitly ordered if (-not $order) { $order = @($allParameters | Select-Object @{ Name = "Name" Expression = { $_ } },@{ Name= "NaturalPosition" Expression = { $p = @($cmdMd.Parameters[$_].ParameterSets.Values)[0].Position if ($p -ge 0) { $p } else { 1gb } } } | Sort-Object NaturalPosition| Select-Object -ExpandProperty Name) } if (-not $cmdParams.Body) { $body = if ($request -and $request.Params -and $request.Params['Body']) { $request.Params['Body'] } else { "" } # The body should strip off the command specifier, so that the input could be easily redirected from a module $possibleCmdNames = @(Get-Alias -Definition "$($cmdMd.Name)" -ErrorAction SilentlyContinue) + "$($cmdMd.Name)" foreach ($pcn in $possibleCmdNames) { if ($body -like "$pcn*") { $body = $body.Susbstring("$pcn".Length).TrimStart(":").TrimStart("-") } } $body = $body.Replace(",", '`,').Replace("(", '`(').Replace(")",'`)').Replace(";", '`;') $dataScriptBlock = [ScriptBlock]::create(" $($cmdMd.Name) @cmdParams $body ") $dataScriptBlock = [ScriptBlock]::Create("data -SupportedCommand $($cmdMd.Name) { $dataScriptBlock } ") try { $error.clear() $outputData = & $dataScriptBlock } catch { $errorRecord = $_ } } else { $error.clear() try { $outputData = & $command @mergedParameters 2>&1 } catch { $errorRecord = $_ } } if ($outputData -is [string]) { $response.contentType = 'text/xml' $response.Write("<?xml version='1.0' encoding='UTF-8'?> <Response> <Sms>$([Security.SecurityElement]::Escape($OutputData))</Sms> </Response>") } elseif ($outputData -is [Management.Automation.ErrorRecord]) { $msg = $OutputData.Message $msg = if ($msg.Length -gt 160) { $msg.Substring(0, 159) } else { $msg } $response.contentType = 'text/xml' $response.Write("<?xml version='1.0' encoding='UTF-8'?> <Response> <Sms>$([Security.SecurityElement]::Escape($msg))</Sms> </Response>") } elseif ($ErrorRecord) { $msg = "$errorRecord" $msg = if ($msg.Length -gt 160) { $msg.Substring(0, 159) } else { $msg } $response.contentType = 'text/xml' $response.Write("<?xml version='1.0' encoding='UTF-8'?> <Response> <Sms>$([Security.SecurityElement]::Escape($msg))</Sms> </Response>") } else { $outputText = "" # Loop thru each object and concatenate properties foreach ($outputItem in $outputData) { foreach ($kv in $outputItem.psObject.properties) { if (-not $kv) { continue } $outputText += "$($kv.Name):$($kv.Value)$([Environment]::NewLine)" } } if ($outputText) { $response.contentType = 'text/xml' $response.Write("<?xml version='1.0' encoding='UTF-8'?> <Response> <Sms>$($outputText)</Sms> </Response>") } } return } elseif ($Request -and $Request["CallSid"] -and $Request["From"] -and $Request["To"] -and $Request["AccountSid"] ) { # Phone handler $callSid = $request["CallSid"] $cmdParams = @{} + $commandParameters if ($parameterDefaultValue) { foreach ($d in $ParameterDefaultValue.Keys) { if (! $cmdParams.ContainsKey($D)) { $cmdParams[$d] = $ParameterDefaultValue[$d] } } } if ($command.Parameters.From -and $request['From']) { $cmdParams["From"] = $request['From'] } if ($command.Parameters.To -and $request['To']) { $cmdParams["To"] = $request['To'] } if ($command.Parameters.AccountSid -and $request['AccountSid']) { $cmdParams["Accountsid"] = $request['Accountsid'] } if ($command.Parameters.CallSid -and $request['CallSid']) { $cmdParams["CallSid"] = $request['CallSid'] } if ($command.Parameters.FromCity -and $request['FromCity']) { $cmdParams["FromCity"] = $request['FromCity'] } if ($command.Parameters.FromState -and $request['FromState']) { $cmdParams["FromState"] = $request['FromState'] } if ($command.Parameters.FromZip -and $request['FromZip']) { $cmdParams["FromZip"] = $request['FromZip'] } if ($command.Parameters.FromCountry -and $request['FromCountry']) { $cmdParams["FromCountry"] = $request['FromCountry'] } if ($command.Parameters.ToCity -and $request['ToCity']) { $cmdParams["ToCity"] = $request['ToCity'] } if ($command.Parameters.ToState -and $request['ToState']) { $cmdParams["ToState"] = $request['ToState'] } if ($command.Parameters.ToZip -and $request['ToZip']) { $cmdParams["ToZip"] = $request['ToZip'] } if ($command.Parameters.ToCountry -and $request['ToCountry']) { $cmdParams["ToCountry"] = $request['ToCountry'] } $allowedParameter = @($cmdMd.Parameters.Keys ) if (-not $AllowedParameter) { $allowedParameter = $cmdMd.Parameters.Keys } # If a parameter set was provided, filter out parameters from other parameter sets if ($parameterSet) { $allParameters = $allowedParameter | Where-Object { $cmdMd.Parameters[$_].Attributes | Where-Object { $_.ParameterSetName -eq $parameterSet } } } $allParameters = foreach ($param in $allowedParameter) { if ($HideParameter -notcontains $param -and (! $CmdParams.ContainsKey($param))) { $param } } # Order parameters if they are not explicitly ordered $order = @($allParameters | Select-Object @{ Name = "Name" Expression = { $_ } },@{ Name= "NaturalPosition" Expression = { $p = @($cmdMd.Parameters[$_].ParameterSets.Values)[0].Position if ($p -ge 0) { $p } else { 1gb } } } | Where-Object { $_.NaturalPosition -ne 1gb -and (! $CmdParams.ContainsKey($_.Name))} | Sort-Object NaturalPosition| Select-Object -ExpandProperty Name) $parameterNumber = if ($request["ParameterNumber"]) { $request["ParameterNumber"] -as [Int32] } else { 0 } # In phone calls, the system needs to keep track of the state of each individual call. # This is done using a cookie keyed off of the CallSid $callSid = $request["CallSid"] $parameterValues = @() for ($pN = 0; $pn -lt $parameterNumber; $pn++) { if ($request["P$Pn"]) { # A parameter value exists! $parameterValues+= $request["P$pn"] } } $helpObj= ($command | Get-Help) $responseXml = "<Response>" if ($parameterNumber -lt $order.Count) { $parameterInfo = $order[$parameterNumber] $parameterInfo = $command.Parameters[$parameterInfo] } else { $parameterInfo = $null } $parameter = $parameterInfo.Name $parameterType = $parameterInfo.ParameterType $validateSet = foreach ($attribute in $parameterInfo.Attributes) { if ($attribute.TypeId -eq [Management.Automation.ValidateSetAttribute]) { $attribute break } } $parameterHelp = foreach ($p in $helpObj.Parameters.Parameter) { if ($p.Name -eq $parameter) { $p.Description | Select-Object -ExpandProperty Text } } $parameterVisibleHelp = $parameterHelp -split ("[`n`r]") |? { $_ -notlike "|*" } $pipeworksDirectives = @{} foreach ($line in $parameterHelp -split ("[`n`r]")) { if ($line -like "|*") { $directiveEnd= $line.IndexofAny(": `n`r".ToCharArray()) if ($directiveEnd -ne -1) { $name, $rest = $line.Substring(1, $directiveEnd -1).Trim(), $line.Substring($directiveEnd +1).Trim() $pipeworksDirectives.$Name = $rest } else { $name = $line.Substring(1).Trim() $pipeworksDirectives.$Name = $true } } } if ($request.Cookies["CallStatusFor_${CallSid}"]) { $callStatusCookie = $request.Cookies["CallStatusFor_${CallSid}"] } else { $callStatusCookie = New-Object Web.HttpCookie "CallStatusFor_${CallSid}" } if ($pipeworksDirectives.Options -or $validateSet -or ($parameterType -and $parameterType.IsSubClassOf([Enum]))) { $optionList = if ($pipeworksDirectives.Options) { Invoke-Expression -Command "$($pipeworksDirectives.Options)" -ErrorAction SilentlyContinue } elseif ($ValidateSet) { $ValidateSet.ValidValues } elseif ($parameterType -and $parameterType.IsSubClassOf([Enum])) { [Enum]::GetValues($parameterType) } } else { $optionList = @() } if ($parameterInfo -and $Request["TranscriptionText"]) { # The person has supplied a value to a parameter, store it and move on if ($parameterInfo.ParameterType -eq [ScriptBlock]) { $parameterValues += try { [ScriptBlock]::Create($Request["TranscriptionText"]) } catch {} } else { $parameterValues += ($Request["TranscriptionText"] -as $parameterInfo.ParameterType) } $parameterValues += $Request["TranscriptionText"] $callStatusCookie["ParameterValue_$parameterInfo"] = $Request["RecordingUrl"] $parameterNumber++ } elseif ( $Request["RecordingUrl"]) { # A recording $callStatusCookie["ParameterValue_$parameterInfo"] = $Request["RecordingUrl"] $parameterValues += $Request["RecordingUrl"] $parameterNumber++ } elseif ($Request["Digits"]) { # The person has supplied a value to a parameter, add it to the cookie and move on $callStatusCookie["ParameterValue_$parameterInfo"] = $request["Digits"].TrimEnd("#").TrimEnd("*") $digit = $request["Digits"].TrimEnd("#").TrimEnd("*") -as [Uint32] if ($optionList -and $optionList[$digit - 1]) { $parameterValues += $optionList[$digit - 1] } else { $parameterValues += $digit } $parameterNumber++ } else { if ($parameterNumber -eq 0 ) { $description = if ($helpObj.Description) { $helpObj.Description[0].text.Replace('"',"'").Replace('<', '<').Replace('>', '>').Replace('$', '`$') } else { "" } if ($description) { if ($description -like "http://*") { $responseXml += "<Play>$([Security.SecurityElement]::Escape($description))</Play>" } else { $responseXml += "<Say>$([Security.SecurityElement]::Escape($description))</Say>" } } } } if ((-not $order.Count) -or ($parameterNumber -eq $order.Count)) { $params = @{} for ($on = 0 ; $on -lt $order.count; $on++) { $paramInfo = $Command.Parameters[$order[$on]] if ($paramInfo.ParameterType -eq [switch]) { $params[$order[$on]] = if ($parameterValues[$on] -eq 0) { $false } else { $true } } else { $params[$order[$on]] = $parameterValues[$on] } } foreach ($p in $params.GetEnumerator()) { $cmdParams[$p.Key] = $p.Value } foreach ($k in @($cmdParams.Keys)) { if (-not $command.Parameters.ContainsKey($k)) { $cmdParams.remove($k)# = $p.Value } } $cmdResult = & $command @cmdParams if ($cmdResult -as [xml]) { $xml = ($cmdResult -as [xml]) if ($xml.Response) { $responseXml += $xml.Response.InnerXml } else { $responseXml += $xml.InnerXml } } elseif ($cmdResult -like "http*") { if ($cmdResult -like "*.mp3") { $responseXml += "<Play>$([Security.SecurityElement]::Escape($cmdResult))</Play>" } elseif ($cmdResult -like "*.wav") { $responseXml += "<Play>$([Security.SecurityElement]::Escape($cmdResult))</Play>" } else { $responseXml += "<Redirect>$([Security.SecurityElement]::Escape($cmdResult))</Redirect>" } } else { $responseXml += "<Say>$([Security.SecurityElement]::Escape($cmdResult))</Say>" } } else { $parameterInfo = $order[$parameterNumber] $parameterInfo = $Command.Parameters[$parameterInfo] $parameter = $parameterInfo.Name $parameterType = $parameterInfo.ParameterType # Combine the parameters here, so we can send the handler back to the right spot with all of the data intact $ParameterData = if ($parameterValues.Count) { for ($pN = 0; $pn -le $parameterValues.Count; $pn++) { "P$PN=$([web.httputility]::UrlEncode($parameterValues[$pn]))" } } else { "" } $ParameterData = $ParameterData -join "&" $actionUrl = if ($fullUrl.Contains("ParameterNumber=")) { # Replacing an existing parameter data $fullUrl ="$fullUrl".substring(0,"$fullUrl".IndexOf("ParameterNumber=") -1) if ($fullUrl.ToString().Contains("?")) { $fullUrl + "&ParameterNumber=$ParameterNumber&${ParameterData}" } else { "$fullUrl".TrimEnd("/") + "/" + "?ParameterNumber=$ParameterNumber&${ParameterData}" } } elseif ($fullUrl.ToString().Contains("?")) { $fullUrl + "&ParameterNumber=$ParameterNumber&${ParameterData}" } else { "$fullUrl".TrimEnd("/") + "/" + "?ParameterNumber=$ParameterNumber&${ParameterData}" } # Request input for the parameter. $parameterInfo = $order[$parameterNumber] $paramInput = Request-CommandInput -Platform TwilML -CommandMetaData $cmdMd -AllowedParameter $parameter -Action $actionUrl -DenyParameter $HideParameter if ($paramInput -as [xml]) { $responseXml += ($paramInput -as [xml]).Response.innerXml } } # If the parameter is a Number, have them enter it in on the keypad. # If the parameter is text, and named Recording* or *Recording, then record # If the parameter is text, and named Trascribe* or *Transcript, then transcribe # Otherwise, transcribe # If the parameter is a PSObject, pass in the whole recording or transcription object #$responseXml += "<Play>$([Security.SecurityElement]::Escape($ActionUrl))</Play>" if ($callStatusCookie) { $response.Cookies.Add($callStatusCookie) } $responseXml += "</Response>" $strWrite = New-Object IO.StringWriter ([xml]$responseXml).Save($strWrite) $resultToOutput = "$strWrite" -replace "encoding=`"utf-16`"", "encoding=`"utf-8`"" if (-not $cmdOptions.ContentType) { $response.ContentType ="text/xml" } $response.Write("$resultToOutput") } elseif ($requestParameterNames."DownloadProxy") { # Start with the core command, . $getProxyCommand if ($Request["AsHtml"]) { $webPage = Write-ScriptHtml ([ScriptBlock]::create($proxyCommandText)) $webPage } elseif ($request["AsBase64"]) { $b64 = [Convert]::ToBase64String(([Text.Encoding]::Unicode.GetBytes($proxyCommandText))) $response.ContentType = "text/plain" $response.Write($b64) $response.Flush() } else { #$proxyCommandText $response.ContentType = "text/plain" $response.Write($proxyCommandText) $response.Flush() } } else { # Normal command handler # If they want a link to run the command, instead of actually running it... if ($Request -and ($Request['GetLink'] -or $request['-GetLink'])) { # Get a link $response.contentType = 'text/plain' $responseString = $request.Url.ToString() if ($responseString.Contains('?')) { $responseString = $responseString.Substring(0, $responseString.IndexOf("?")) } $responseString+='?' foreach ($cp in $commandParameters.GetEnumerator()) { $b64 = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cp.Value)) $responseString +="$($CmdMd.Name)_$($cp.Key)=${b64}&" } $responseString return } # If -Bare/Bare was specified, then do not output anything but the result if ($Request -and (($request['Bare'] -ilike "$true") -or ($request['-Bare'] -ilike "$true"))) { $cmdoptions.PlainOutput = $true } $result = $null $runAnyways = ($request -and ( $request["RunItAnyway"] -or $request["RunItAnyways"] -or $request["RunAnyway"] -or $request["RunAnyways"])) if ($cmdOptions.runWithoutInput -or ($CommandParameters.Count -gt 0) -or $runAnyways) { if ($cmdOptions.RequireAppKey) { $appKey = if ($session['User'].SecondaryApiKey -and -not $request['AppKey']) { $session['User'].SecondaryApiKey } elseif ($request['AppKey']) { $request['AppKey'] } else { Write-Error "App Key is Required. The user must be logged in, or a parameter named AppKey must be passed with the request" return } Confirm-Person -ApiKey $appKey -StorageAccountSetting $storageAccountSetting -StorageKeySetting $storageKeySetting -WebsiteUrl $serviceUrl <# $storageAccount = Get-WebConfigurationSetting -Setting $cmdOptions.StorageAccountSetting $storageKey = Get-WebConfigurationSetting -Setting $cmdOptions.StorageKeySetting $userTableExists = Get-AzureTable -TableName $cmdOptions.UserTable -StorageAccount $storageAccount -StorageKey $storageKey if (-not $userTableExists) { return } $userExists = Search-AzureTable -TableName $cmdOptions.UserTable -Filter "PartitionKey eq '$($cmdOptions.UserPartition)' and SecondaryApiKey eq '$appKey'" if (-not $UserExists) { Write-Error "User not found" return } #> } elseif ($cmdOptions.RequireLogin) { $confirmHtml = Confirm-Person -WebsiteUrl $serviceUrl if ($session["User"]) { $userExists = $session['User'] } else { if ($confirmHtml) { $confirmHtml } return } } # There are parameters, or the command knows it can run without them. # First, clear out empty parameters from the structure if ($mergedParameters.Count) { $toRemove = @() foreach ($kv in $mergedParameters.GetEnumerator()) { if (-not $kv.Value -and ($kv.Value -ne 0)) { $toRemove += $kv.Key } } foreach ($to in $toRemove) { $null = $mergedParameters.Remove($to) } } # Then, Enforce the Session Throttle $doNotRunCommand = $true if ($cmdOptions.SessionThrottle.TotalMilliseconds) { if (-not $session["$($cmdMd.Name)_LastRun"]) { $session["$($cmdMd.Name)_LastRun"] = Get-Date $doNotRunCommand = $false } elseif (($session["$($cmdMd.Name)_LastRun"] + $cmdOptions.SessionThrottle) -lt (Get-Date)) { $session["$($cmdMd.Name)_LastRun"] = Get-Date $doNotRunCommand = $false } else { $timeUntilICanRunAgain = (Get-Date) - ($session["$($cmdMd.Name)_LastRun"] + $cmdOptions.SessionThrottle) "<span style='color:red'>Can run the command again in $(-$timeUntilICanRunAgain.TotalSeconds) Seconds</span>" $doNotRunCommand = $true } } else { $doNotRunCommand = $false } if ($mergedParameters.Count -gt 0 -or $psBoundParameters.RunWithoutInput) { } # Default behavior, do not cache if (-not $doNotRunCommand) { # Run the command $useId = [GUID]::NewGuid() if ($cmdOptions.ModeratedBy -and $cmdOptions.ExecutionQueueTable) { # If the command was moderated, 'running' the command is really putting # the parameters into table storage for someone to approve $subject = "Would you like to run $($cmdMd.Name)?" $confirmId = $useId $pendingExecutionRequest = New-Object PSObject -Property $commandParameters $requestAsString = $request.Url.ToString() $serviceUrl = $requestAsString.Substring(0,$requestAsString.LastIndexOf("/")) $canReply = if ($session['User'].UserEmail) { "<a href='$serviceUrl?sendreply=$confirmId'>Reply</a>" } else { "" } $message = " $userInfo has requested that you run $($cmdMd.Name) with the following parameters: $($pendingExecutionRequest | Out-HTML) <a href='${finalUrl}?confirm=$confirmId'>Run this</a> <a href='${finalUrl}?deny=$confirmId'>Don't run this</a> $canReply " $smtpEmail = Get-SecureSetting $cmdOptions.SmtpEmailSetting -ValueOnly $smtpPassword = Get-SecureSetting $cmdOptions.SmtpPasswordSetting -ValueOnly $smtpCred = New-Object Management.Automation.PSCredential ".\$smtpEmail", "$(ConvertTo-SecureString -AsPlainText -Force $smtpPassword)" Send-MailMessage -To $cmdOptions.ModeratedBy -UseWebConfiguration -Subject $subject -Body $Message -BodyAsHtml -AsJob $pendingExecutionRequest | Set-AzureTable -TableName $cmdOptions.ExecutionQueueTable -PartitionKey $cmdMd.Name -RowKey $useId $result = "Your request has been sent to the moderator" } elseif (($PayNow -or $SubscribeNow) -and ($Request -and $Request["Confirmed"])) { # Calculate total payment and present with a bill and a pay now link. When the payment is processed the command will be run . $ComputeTotalCost $confirmLink = "$serviceUrl".Substring(0, "$serviceUrl".LastIndexOf("/")) + "/" + $Command + "/" if ($confirmLink.Contains("?")) { $confirmLink += "&" } else { $confirmLink = $confirmLink.TrimEnd("/") + "/?" } $costString = "$" + $totalCost $parameterConfirm = " <span style='font-size:2em'>Are you sure you'd like to $($FriendlyName)?. It will cost $costString .</span> " $parameterConfirm += "<div style='margin-left:auto;margin-right:auto;width:66%'> <table style='align:center'> <tr> <th style='font-size:1.6em;width:50%'> Option </th> <th style='font-size:1.6em;width:50%'> Value </th> </tr>" foreach ($pValue in $commandParameters.GetEnumerator()) { if (-not $pValue) { continue } $confirmLink += "&$($Command)_$($pValue.Key)=$([Web.HttpUtility]::UrlEncode($pValue.Value))" $parameterConfirm += "<tr><td><b>$($pValue.Key)</b><td><td>$($pValue.Value | Out-HTML)</td></tr>" } $confirmLink += "&Confirmed=true" $parameterConfirm +="</table></div>" $parameterConfirm += Write-Link -Url $confirmLink -Caption "OK" -Style @{'font-size' = '2.5em';float='right'} $parameterConfirm | New-WebPage | Out-HTML -WriteResponse return } elseif ((($PayNow -or $SubscribeNow) -and (-not ($Request -and $request["Confirmed"])))) { # Prompting for a pay link if -AsEmail is not provided. . $ComputeTotalCost $postPaymentCommand = $Command $session["PostPaymentCommand"] = $postPaymentCommand $session["PostPaymentParameter"] = $PostPaymentParameter = Write-PowerShellHashtable -InputObject $CommandParameters #$PostPaymentParameter = [Web.HttpUtility]::UrlEncode($PostPaymentParameter) $payLink = if ($paynow) { "Purchase" } elseif ($SubscribeNow) { "Rent" } $payUrl = $ServiceUrl.ToString() + "?$payLink=True&ItemName=$Command&ItemPrice=$TotalCost&$(if ($SubscribeNow) { "BillingFrequency=$BillingFrequency" })" #$PostPaymentParameter,$postPaymentCommand = $null $userPart = if ($pipeworksManifest.UserTable.Partition) { $pipeworksManifest.UserTable.Partition } else { "Users" } $purchaseHistory = $userPart + "_Purchases" $azureStorageAccount = Get-SecureSetting -Name $pipeworksManifest.UserTable.StorageAccountSetting -ValueOnly $azureStorageKey= Get-SecureSetting -Name $pipeworksManifest.UserTable.StorageKeySetting -ValueOnly $purchase = @() $payLinks = "" $fullPayLink = "" if (-not $listOfCosts) { $itemPrice = $TotalCost $purchaseId = [GUID]::NewGuid() $newpurchase = New-Object PSObject $newpurchase.pstypenames.clear() $newpurchase.pstypenames.add('http://shouldbeonschema.org/ReceiptItem') $itemNAme = $FriendlyName $Currency = if ($cmdOptions.Currency) { $cmdOptions.Currency } else { "USD" } $newpurchase = $newpurchase | Add-Member NoteProperty PurchaseId $purchaseId -PassThru | Add-Member NoteProperty ItemName $itemName -PassThru | Add-Member NoteProperty ItemPrice $itemPrice -PassThru | Add-Member NoteProperty Currency $currency -PassThru | Add-Member NoteProperty OrderTime $request["OrderTime"] -PassThru if ($session["User"]) { $newpurchase = $newpurchase | Add-Member NoteProperty UserID $session["User"].UserID -PassThru } $isRental = if ($SubscribeNow) { $true } else { $false } $newpurchase = $newpurchase | Add-Member NoteProperty IsRental $isRental -Force -PassThru if ($postPaymentCommand) { $newpurchase = $newpurchase | Add-Member NoteProperty PostPaymentCommand $postPaymentCommand -PassThru } if ($PostPaymentParameter) { $newpurchase = $newpurchase | Add-Member NoteProperty PostPaymentParameter $PostPaymentParameter -PassThru } $newpurchase | Set-AzureTable -TableName $pipeworksManifest.UserTable.Name -PartitionKey $purchaseHistory -RowKey $purchaseId -StorageAccount $azureStorageAccount -StorageKey $azureStorageKey $payLinks = "" $payLinks += if ($pipeworksManifest.PaymentProcessing.AmazonPaymentsAccountId -and $pipeworksManifest.PaymentProcessing.AmazonAccessKey) { $newpurchase | Write-Link -AmazonPaymentsAccountId $pipeworksManifest.PaymentProcessing.AmazonPaymentsAccountId -AmazonAccessKey $pipeworksManifest.PaymentProcessing.AmazonAccessKey } $payLinks += if ($pipeworksManifest.PaymentProcessing.PaypalEmail) { Write-Link -ItemName $itemName -Currency $currency -ItemPrice $itemPrice -PaypalEmail $pipeworksManifest.PaymentProcessing.PaypalEmail -PaypalIPN "$ServiceUrl/?-PaypalIPN" -PaypalCustom $PurchaseId -Subscribe:$isRental -BillingFrequency $BillingFrequency } $payLinks += if ($pipeworksManifest.PaymentProcessing.BuyCodeHandler) { $newpurchase | Write-Link -BuyCodeHandler $pipeworksManifest.PaymentProcessing.BuyCodeHandler -Subscribe:$isRental } $fullPayLink += " <span style='font-size:1.3em'>$itemName</span> <br/> <span style='font-size:1.1em'>$itemPrice $currency $(if ($SubscribeNow) { " $BillingFrequency"})</span> <br/> $($payLinks| Out-HTML)" } else { $lc = 0 foreach ($loc in $listOfCosts) { $itemPrice = $loc $purchaseId = [GUID]::NewGuid() $newpurchase = New-Object PSObject $newpurchase.pstypenames.clear() $newpurchase.pstypenames.add('http://shouldbeonschema.org/ReceiptItem') $itemNAme = $FriendlyName $TheCurrency = if ($cmdOptions.Currency -and $cmdOptions.Currency.Count -ge $lc) { $cmdOptions.Currency[$lc] } else { $Currency[0] } $TheBillingFrequency= if ($cmdOptions.BillingFrequency -and $cmdOptions.BillingFrequency.Count -ge $lc) { $cmdOptions.BillingFrequency[$lc] } else { $BillingFrequency[0] } $newpurchase = $newpurchase | Add-Member NoteProperty PurchaseId $purchaseId -PassThru | Add-Member NoteProperty ItemName $itemName -PassThru | Add-Member NoteProperty ItemPrice $itemPrice -PassThru | Add-Member NoteProperty Currency $TheCurrency -PassThru | Add-Member NoteProperty BillingFrequency $TheBillingFrequency -PassThru if ($session["User"]) { $newpurchase = $newpurchase | Add-Member NoteProperty UserID $session["User"].UserID -PassThru } $isRental = if ($SubscribeNow) { $true } else { $false } $newpurchase = $newpurchase | Add-Member NoteProperty IsRental $isRental -Force -PassThru if ($postPaymentCommand) { $newpurchase = $newpurchase | Add-Member NoteProperty PostPaymentCommand $postPaymentCommand -PassThru } if ($PostPaymentParameter) { $newpurchase = $newpurchase | Add-Member NoteProperty PostPaymentParameter $PostPaymentParameter -PassThru } $purchase += $newpurchase $itemPrice = $newpurchase.ItemPrice $TheCurrency = $newpurchase.Currency $TheBillingFrequency = $newpurchase.BillingFrequency $newpurchase | Set-AzureTable -TableName $pipeworksManifest.UserTable.Name -PartitionKey $purchaseHistory -RowKey { $_.purchaseId } -StorageAccount $azureStorageAccount -StorageKey $azureStorageKey $payLinks = "" $payLinks += if ($pipeworksManifest.PaymentProcessing.AmazonPaymentsAccountId -and $pipeworksManifest.PaymentProcessing.AmazonAccessKey) { $newpurchase | Write-Link -AmazonPaymentsAccountId $pipeworksManifest.PaymentProcessing.AmazonPaymentsAccountId -AmazonAccessKey $pipeworksManifest.PaymentProcessing.AmazonAccessKey } $payLinks += if ($pipeworksManifest.PaymentProcessing.PaypalEmail) { Write-Link -ItemName $itemName -Currency $TheCurrency -ItemPrice $itemPrice -PaypalEmail $pipeworksManifest.PaymentProcessing.PaypalEmail -PaypalIPN "$ServiceUrl/?-PaypalIPN" -PaypalCustom $PurchaseId -Subscribe:$isRental -BillingFrequency:$TheBillingFrequency } $payLinks += if ($pipeworksManifest.PaymentProcessing.BuyCodeHandler) { $newpurchase | Write-Link -BuyCodeHandler $pipeworksManifest.PaymentProcessing.BuyCodeHandler -Subscribe:$isRental } $lc++ $fullPayLink += " <span style='font-size:1.3em'>$itemName</span> <br/> <span style='font-size:1.1em'>$itemPrice $Thecurrency $(if ($SubscribeNow) { " $TheBillingFrequency"})</span> <br/> $($payLinks| Out-HTML)" } } if ($cmdOptions.PaymentProcessed) { try { if ($request -and ($Request["OutputParameter"] -eq 'true')) { $result = $mergedParameters } else { $result = & $command @mergedParameters -ErrorVariable AnyProblem } return ($result | Out-HTML) } catch { $commandError = $_ } } else { return $fullPayLink } } elseif ($promptForConfirmation -and -not $request["Confirmed"]) { # Prompt for the person's confirmation $confirmLink = "$fullUrl" if (-not $ConfirmationMessage) { $ConfirmationMessage= "Are you sure you'd like to $($FriendlyName)?." } $parameterConfirm = " <span style='font-size:2em'>$ConfirmationMessage</span> " $parameterConfirm += "<div style='margin-left:auto;margin-right:auto;width:66%'> <table style='align:center'> <tr> <th style='font-size:1.6em;width:50%'> Option </th> <th style='font-size:1.6em;width:50%'> Value </th> </tr>" foreach ($pValue in $commandParameters.GetEnumerator()) { if (-not $pValue) { continue } $confirmLink += "&$($Command)_$($pValue.Key)=$([Web.HttpUtility]::UrlEncode($pValue.Value))" $parameterConfirm += "<tr><td><b>$($pValue.Key)</b><td><td>$($pValue.Value | Out-HTML)</td></tr>" } $confirmLink += "&Confirmed=true" $parameterConfirm +="</table></div>" $parameterConfirm += Write-Link -Url $confirmLink -Caption "OK" -Style @{'font-size' = '2.5em';float='right';margin='100px'} $parameterConfirm | New-WebPage | Out-HTML -WriteResponse return } else { $anyProblem = "" $commandError = $null try { if ($request -and ($Request["OutputParameter"])) { $result = $mergedParameters } elseif ($request -and $request["OutputParameterType"]) { foreach ($mp in @($mergedParameters.GetEnumerator())) { $mergedParameters[$mp.Key] = if ($mp.Value) { $mp.Value.GetType() } } $result = $mergedParameters } else { $result = & $command @mergedParameters -ErrorVariable AnyProblem } if ($request -and $request["OutputError"]) { $result = $anyProblem } } catch { $commandError = $_ if ($manifestIsTemporary) { return } if ($PlainOutput) { return $commandError } else { $result = $commandError } } } $worked = $? $cmdresult = $result $commandWasRun = $true # If it worked, charge them if ($worked -and $userExists -and ($cmdOptions.Cost -or $cmdOptions.CostFactor)) { $userRecord = Search-AzureTable -TableName $cmdOptions.UserTable -Filter "PartitionKey eq '$($cmdOptions.UserPartition)' and RowKey eq '$($userExists.UserId)'" if ($cmdOptions.Cost -and -not $PaymentProcessed) { # If there was a fixed cost, apply this cost to the user $balance = $userRecord.Balance -as [Double] $balance += $cmdOptions.Cost -as [Double] $userRecord | Add-Member NoteProperty Balance $balance -Force -PassThru | Update-AzureTable -TableName $cmdOptions.UserTable -Value { $_ } } if ($cmdOptions.CostFactor -and -not $PaymentProcessed) { $factoredCost = 0 foreach ($kv in $cmdOptions.CostFactor.getEnumerator()) { $parameterValue = $mergedParameters["$($kv.Value.Parameter)"] if ($kv.Value.CostMap) { $factoredCost += $kv.Value.CostMap[$parameterValue] } elseif ($kv.Value.CostPerValue) { $factoredCost += $kv.Value.CostPerValue * $parameterValue } } # If there was a fixed cost, apply this cost to the user $balance = $userRecord.Balance -as [Double] if (-not $balance) { $balance = 0 } $balance += $factoredCost $userRecord | Add-Member NoteProperty Balance $balance -Force -PassThru | Update-AzureTable -TableName $cmdOptions.UserTable -Value { $_ } } } # Immediately track its use before it is rendered, if the cmdoptions say so if ($cmdOptions.UseTrackingTable) { $storageAccount = Get-WebConfigurationSetting -Setting $cmdOptions.StorageAccountSetting $storageKey = Get-WebConfigurationSetting -Setting $cmdOptions.StorageKeySetting $trackingTable = Get-AzureTable -TableName $cmdOptions.UseTrackingTable -StorageAccount $storageAccount -StorageKey $storageKey if (-not $trackingTable) { return } $useInfo = New-Object PSObject -Property @{ UseId = $useID Worked = $worked } if (-not $worked) { $ht = Write-PowerShellHashtable -InputObject $commandParameters $useInfo | Add-Member NoteProperty Parameters $ht -Force } if ($session['User'].UserId) { $useInfo | Add-Member NoteProperty UserId $session['User'].UserId -Force } if ($appKey) { $useInfo | Add-Member NoteProperty AppKey $appKey -Force } $useInfo | Set-AzureTable -TableName $cmdoptions.UseTrackingTable -PartitionKey "$($cmdMd.Name)_TimesUsed" -RowKey $useId if ($cmdOptions.TrackProperty) { $result | Select-Object $cmdOptions.TrackProperty | ForEach-Object { $_.psobject.properties } | ForEach-Object { $propName = $_.Name $md5 = [Security.Cryptography.MD5]::Create() $content = [Text.Encoding]::Unicode.GetBytes(("$($_.Value)")) $part = [BitConverter]::ToString($md5.ComputeHash($content)) $useInfo | Set-AzureTable -TableName $cmdoptions.UseTrackingTable -PartitionKey "$($cmdMd.Name)_$($propName)_${Part}" -RowKey $useID } } if ($cmdOptions.TrackParameter) { New-Object PSObject -Property $commandParameters | Select-Object $cmdOptions.TrackParameter | ForEach-Object { $_.psobject.properties } | ForEach-Object { $propName = $_.Name $md5 = [Security.Cryptography.MD5]::Create() $content = [Text.Encoding]::Unicode.GetBytes(("$($_.Value)")) $part = [BitConverter]::ToString($md5.ComputeHash($content)) $useInfo | Set-AzureTable -TableName $cmdoptions.UseTrackingTable -PartitionKey "$($cmdMd.Name)_$($propName)_Input_${Part}" -RowKey $useID } } if ($cmdOptions.KeepResult -and $result) { if ($session['User'].UserId) { $result | Add-Member NoteProperty __UserId $session['User'].UserId -Force } if ($appKey) { $result | Add-Member NoteProperty __AppKey $appKey -Force } $result | Add-Member NoteProperty __UseId $useId -Force $result | Set-AzureTable -TableName $cmdoptions.UseTrackingTable -PartitionKey "$($cmdMd.Name)_Results" -RowKey $useID } if ($cmdOptions.KeepUserHistory) { $ht = Write-PowerShellHashtable -InputObject $commandParameters $md5 = [Security.Cryptography.MD5]::Create() $content = [Text.Encoding]::Unicode.GetBytes(("$($ht)")) $part = [BitConverter]::ToString($md5.ComputeHash($content)) New-Object PSObject -Property @{ UseId = $useID CompressedInput = Compress-Data -String $ht } $useInfo | Set-AzureTable -TableName $cmdoptions.UseTrackingTable -PartitionKey "$($cmdMd.Name)_$($propName)_Input_${Part}" -RowKey $useID } if ($cmdOptions.KeepHistory) { $ht = Write-PowerShellHashtable -InputObject $commandParameters $md5 = [Security.Cryptography.MD5]::Create() $content = [Text.Encoding]::Unicode.GetBytes(("$($ht)")) $part = [BitConverter]::ToString($md5.ComputeHash($content)) New-Object PSObject -Property @{ UseId = $useID CompressedInput = Compress-Data -String $ht CompressedResult = Compress-Data -String ($result | ConvertTo-Xml) } } } if (-not $result -and $NoOutputMessage) { $result = $NoOutputMessage } else { if ($OutputProperty) { $result = $result | Select-Object $OutputProperty } } if ($LinkTo) { $result = foreach ($r in $result) { if (-not $r) { continue } foreach ($lt in $linkTo.GetEnumerator()) { # The key of the link is the command / parameter, and the value is the property, for instance: # @{"Get-Employee/RowKey" = "Row"} if ($r.psobject.properties.($lt.Value)) { $propCaption = $r.($lt.Value) $moduleUrlRoot = $ServiceUrl.ToString().Substring(0, $ServiceUrl.ToString().LastIndexOf("/")) $link = if ($lt.Key -like "*/*") { $cmdName ,$paramName = $lt.Key -split "/" "$moduleUrlRoot/$cmdName/?$($cmdname)_$($paramName)=$($r.($lt.Value))" } else { $cmdName = $lt.Key "$moduleUrlRoot/$cmdName/$($lt.Key)" } $linkHtml = Write-Link -Caption $propCaption -Url $link Add-Member -InputObject $r -Name $lt.Value -MemberType NoteProperty -Value $linkHtml -Force } } $r } } if ($AsTypename) { $result = foreach ($r in $result) { if (-not $r) { continue } $r.pstypenames.clear() $R.pstypenames.add($AsTypename) $r } } if ($Request -and (($request['AsXml'] -eq $true) -or ($request['-AsXml'] -eq $true))) { $response.contentType = 'text/xml' $result = [string]($result | ConvertTo-Xml -as String) $response.Write("$result ") return } if ($Request -and (($request['AsCsv'] -eq $true) -or ($request['-AsCsv'] -eq $true))) { $response.contentType = 'text/csv' $csvFile = [io.path]::GetTempFileName() + ".csv" $result | Export-Csv -Path $csvFile $response.Write("$([IO.File]::ReadAllText($csvFile))") Remove-Item -Path $csvFile -ErrorAction SilentlyContinue return } if ($Request -and (($request['AsJson'] -eq $true) -or ($request['-AsJson'] -eq $true))) { if ($PSVersionTable.PSVersion -ge '3.0') { $response.ContentType -eq 'application/json' $response.Write("$($result | ConvertTo-Json)") return } else { throw "Not PS v3 or greater" } } # $result | Out-HTML -Id "${CommandId}Output" -Escape:$escape if ($Request -and (($request['AsRss'] -eq $true) -or ($request['-AsRss'] -eq $true))) { $response.contentType = 'text/xml' $requestAsString = $request.Url.ToString() $pageUrl = $requestAsString -ireplace "AsRss=true", "" $pageUrl = $pageUrl.TrimEnd("&") $shorturl = $requestAsString.Substring(0, $requestAsString.IndexOf("?")) $description = (Get-Help $command.Name).Description if ($description) { $description = $description[0].text.Replace('"',"'").Replace('<', '<').Replace('>', '>').Replace('$', '`$') } $getDateScript = { if ($_.DatePublished) { [DateTime]$_.DatePublished } elseif ($_.TimeCreated) { [DateTime]$_.TimeCreated } elseif ($_.TimeGenerated) { [DateTime]$_.TimeGenerated } elseif ($_.Timestamp) { [DateTime]$_.Timestamp } else { Get-Date } } # DCR: Make RSS support multiple results $resultFeed = $result | Sort-Object $getDateScript -Descending | New-RssItem -DatePublished $getDateScript -Author { if ($_.Author) { $_.Author } else { "$($command.Name)" } } -Link { $pageUrl } -Title { if ($_.Title) { $_.Title } elseif ($_.Name) { $_.Name } else { "$($command.Name) - $(Get-Date)" } } -Description { if ($_.Description) { $_.Description } elseif ($_.ArticleBody) { $_.ArticleBody } elseif ($_.Readings) { $_.Readings | Out-HTML } else { $_ | Out-HTML } } | Out-RssFeed -Link $shorturl -Title "$($command.Name)" -Description "$description" $response.Write("$resultFeed") return } if ($cmdOptions.PlainOutput -or ($request -and ($request['Bare'] -like "$true")) -or ($request -and ($request['-Bare'] -like "$true"))) { if ($cmdOptions.ContentType -and $response) { $response.ContentType = $cmdOptions.ContentType } $resultAsBytes = $result -as [Byte[]] if ($resultAsBytes) { if ($response) { # If the result was a set of bytes, flush them all as one so that the handler can produce a complex content type. $response.BufferOutput = $true $response.BinaryWrite($resultAsBytes ) $response.Flush() } else { $resultAsBytes } } else { # If the result is an XmlDocument, then render it as XML. if ($result -is [xml]) { $strWrite = New-Object IO.StringWriter $result.Save($strWrite) $result = "$strWrite" # And, if the content type hasn't been set, set the content type $resultToOutput = "$strWrite" -replace "encoding=`"utf-16`"", "encoding=`"utf-8`"" if (-not $cmdOptions.ContentType) { $response.ContentType ="text/xml" $response.write($resultToOutput) } } else { if ($cmdOptions.ContentType -and $commandWasRun -and $worked -and (-not $inputWasRequested) -and $response) { $response.ContentType =$cmdOptions.ContentType if ($global:ContentDisposition) { $null = $response.Headers.Add('content-disposition', $global:ContentDisposition.ToString()) $global:ContentDisposition = $null } $response.write($result) } else { # The normal output, with plain output $result } } } if ($manifestIsTemporary) { $global:pipeworksManifest = $null } return } if ($cmdOptons.showCode) { $result | Out-HTML " $([Security.SecurityElement]::Escape($result)) <hr/> Code <br/> <textarea cols='80' rows='30'>$([Security.SecurityElement]::Escape($result))</textarea>" } if ($result) { # Output page # If there's a content type, and the output is that content type, then we want to write the output directly. # This makes services that create images or other files possible if ($cmdOptions.ContentType -and $commandWasRun -and $worked -and (-not $inputWasRequested)) { $response.ContentType =$cmdOptions.ContentType if ($global:ContentDisposition) { $null = $response.Headers.Add('content-disposition', $global:ContentDisposition.ToString()) $global:ContentDisposition = $null } $response.write($result) } else { $redirectPart = if ($RedirectTo -and $result) { $redirectHref = "$RedirectTo" if( $request["Snug"]) { if ($redirectHref.Contains("?")) { $redirectHref += "&snug=true" } else { $redirectHref += "?snug=true" } } if(-not $RedirectIn) { @" <a href='$redirectHref' style='display:none' id='redirectLink'> </a> <script type="text/javascript"> setTimeout('document.getElementById("redirectLink").click()', 250) </script> "@ } else { @" <a href='$redirectHref' style='display:none' id='redirectLink'> </a> <script type="text/javascript"> setTimeout('document.getElementById("redirectLink").click()', $($RedirectIn.TotalMilliseconds)) </script> "@ } } else { "" } if ($request -and $request["Notify"]) { "$($result | Out-Html) $redirectPart <script type='text/javascript'> if (windows.external != null) { windows.external.notify('$($request["Notify"].Replace("'", "''"))'); } </script> " } elseif ($Notify) { "$($result | Out-Html) $redirectPart <script type='text/javascript'> if (windows.external != null) { windows.external.notify('$($notify.Replace("'", "''"))'); } </script> " } else { "$($result | Out-Html) $redirectPart" } } } } } $depthChunk = if ($depth) { "../" * $depth } else { "" } $useAjax = if ($Request -and $Request["Ajax"]) { $true } if ((-not $result) -or ($commandError -or $validationErrors -or $anyProblem)) { $embedded = $request -and (($Request["Embed"] -ilike "$true") -or ($Request["Bare"] -ilike "$true")) $allHiddenParameters =@() + $HideParameter if ($ParameterDefaultValue) { $allHiddenParameters += $ParameterDefaultValue.Keys } $useAjax = if ($pipeworksManifest.NoAjax -or $cmdOptions.NoAjax -or $cmdOptions.ContentType -or $cmdOptions.RedirectTo -or $cmdOptions.PlainOutput) { if ($request -and $request["Ajax"]) { $true } else { $false } } else { $true } if ($anyProblem -or $validationErrors -or $commandError) { $AnyProblem = $AnyProblem | ForEach-Object { $_.Message } | Select-Object -Unique $validationErrors = $validationErrors | ForEach-Object { $_ } $commandError = $commandError | ForEach-Object { $_ } $problemsPage= $AnyProblem, $validationErrors, $commandError -ne $null | Select-Object -Unique | ForEach-Object { "<span class='ui-state-error' style='color:red;padding:15px;margin:10px;font-size:1.33em'>$($_)</span>" }| Out-HTML if (-not $embedded) { $problemsPage = $problemsPage | New-Region -Style @{} -LayerID CommandOutput $method = "POST" if ($request -and $request["Method"]) { $method = $request["Method"] } $problemsPage += Request-CommandInput -Action "${fullUrl}" -CommandMetaData $Command -DenyParameter $allHiddenParameters -Method $method -Ajax:$useAjax | New-Region -Style @{} -LayerID CommandInput $problemsPage = $problemsPage } "$problemsPage " } else { $method = "POST" if ($request -and $request["Method"]) { $method = $request["Method"] } $inputWasRequested = $true $inputPage = Request-CommandInput -Action "$fullUrl" -CommandMetaData $Command -DenyParameter $allHiddenParameters -Method $method -Ajax:$useAjax if (-not $embedded) { $inputPage = $inputPage | New-Region -Style @{} -LayerID CommandInput " $inputPage " } } } if ($manifestIsTemporary) { $global:pipeworksManifest = $null } return } if ($manifestIsTemporary) { $global:pipeworksManifest = $null } } } |