XmlRpc.psm1
#Requires -Version 2.0 <# .Synopsis This is a compilation of functions for XML RPC requests .Description .Notes Author : Oliver Lipkau <oliver@lipkau.net> 2014-06-05 Initial release #> Add-Type -AssemblyName System.Web function ConvertTo-XmlRpcType { <# .SYNOPSIS Convert Data into XML declared datatype string .DESCRIPTION Convert Data into XML declared datatype string .OUTPUTS string .PARAMETER InputObject Object to be converted to XML string .PARAMETER CustomTypes Array of custom Object Types to be considered when converting .EXAMPLE ConvertTo-XmlRpcType "Hello World" -------- Returns <value><string>Hello World</string></value> .EXAMPLE ConvertTo-XmlRpcType 42 -------- Returns <value><int32>42</int32></value> #> [CmdletBinding()] [OutputType([String])] param( [AllowNull()] [Parameter( Position=1, Mandatory=$true )] $InputObject, [Parameter()] [Array]$CustomTypes ) Begin { Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started" $Objects = @('Object') $objects += $CustomTypes } Process { if ($null -ne $inputObject) { [string]$Type=$inputObject.GetType().Name # [string]$BaseType=$inputObject.GetType().BaseType } else { return "<value></value>" } # Return simple Types if (('Double','Int32','Boolean','False') -contains $Type) { return "<value><$($Type)>$($inputObject)</$($Type)></value>" } # Encode string to HTML if ($Type -eq 'String') { return "<value><$Type>$([System.Web.HttpUtility]::HtmlEncode($inputObject))</$Type></value>" } # Int32 must be casted as Int if ($Type -eq 'Int16') { return "<value><int>$inputObject</int></value>" } if ($type -eq "SwitchParameter") { return "<value><boolean>$inputObject.IsPresent</boolean></value>" } # Return In64 as Double if (('Int64') -contains $Type) { return "<value><Double>$inputObject</Double></value>" } # DateTime if('DateTime' -eq $Type) { return "<value><dateTime.iso8601>$($inputObject.ToString( 'yyyyMMddTHH:mm:ss'))</dateTime.iso8601></value>" } # Loop though Array if(($inputObject -is [Array]) -or ($Type -eq "List``1")) { try { return "<value><array><data>$( [string]::Join( '', ($inputObject | ForEach-Object { if ($null -ne $_) { ConvertTo-XmlRpcType $_ -CustomTypes $CustomTypes } else {} } ) ) )</data></array></value>" } catch { throw } } # Loop though HashTable Keys if('Hashtable' -eq $Type) { return "<value><struct>$( [string]::Join( '', ($inputObject.Keys| Foreach-Object { "<member><name>$($_)</name>$( if ($null -ne $inputObject[$_]) { ConvertTo-XmlRpcType $inputObject[$_] -CustomTypes $CustomTypes } else { ConvertTo-XmlRpcType $null })</member>" } ) ) )</struct></value>" } # Loop though Object Properties if(($Objects -contains $Type) -and ($inputObject)) { return "<value><struct>$( [string]::Join( '', ( ($inputObject | Get-Member -MemberType Properties).Name | Foreach-Object { if ($null -ne $inputObject.$_) { "<member><name>$($_)</name>$( ConvertTo-XmlRpcType $inputObject.$_ -CustomTypes $CustomTypes )</member>" } } ) ) )</struct></value>" } # XML if ('XmlElement','XmlDocument' -contains $Type) { return $inputObject.InnerXml.ToString() } # XML if ($inputObject -match "<([^<>]+)>([^<>]+)</\\1>") { return $inputObject } } End { Write-Verbose "$($MyInvocation.MyCommand.Name):: Function Ended" } } function ConvertTo-XmlRpcMethodCall { <# .SYNOPSIS Create a XML RPC Method Call string .DESCRIPTION Create a XML RPC Method Call string .INPUTS string array .OUTPUTS string .PARAMETER Name Name of the Method to be called .PARAMETER Params Parameters to be passed to the Method .PARAMETER CustomTypes Array of custom Object Types to be considered when converting .EXAMPLE ConvertTo-XmlRpcMethodCall -Name updateName -Params @('oldName', 'newName') ---------- Returns (line split and indentation just for conveniance) <?xml version=""1.0""?> <methodCall> <methodName>updateName</methodName> <params> <param><value><string>oldName</string></value></param> <param><value><string>newName</string></value></param> </params> </methodCall> #> [CmdletBinding()] [OutputType( [string] )] param( [Parameter(Mandatory = $true)] [String]$Name, [Parameter()] [Array]$Params, [Parameter()] [Array]$CustomTypes ) Begin {} Process { [String]((&{ "<?xml version=""1.0""?><methodCall><methodName>$($Name)</methodName><params>" if($Params) { $Params | ForEach-Object { "<param>$(&{ConvertTo-XmlRpcType $_ -CustomTypes $CustomTypes})</param>" } } else { "$(ConvertTo-XmlRpcType $NULL)" } "</params></methodCall>" }) -join('')) } End {} } function Send-XmlRpcRequest { <# .SYNOPSIS Send a XML RPC Request .DESCRIPTION Send a XML RPC Request .INPUTS string array .OUTPUTS XML.XmlDocument .EXAMPLE Send-XmlRpcRequest -Url "example.com" -MethodName "updateName" -Params @('oldName', 'newName') --------- Description Calls a method "updateName("oldName", "newName")" on the server example.com #> [CmdletBinding()] [OutputType([Xml.XmlDocument])] param( [Parameter(Mandatory = $true)] [String]$Url, [Parameter(Mandatory = $true)] [String]$MethodName, [Parameter()] [Array]$Params, [Parameter()] [Array]$CustomTypes, $methodCall ) Begin {} Process { if (!$methodCall) { $methodCall = ConvertTo-XmlRpcMethodCall $MethodName $Params -CustomTypes $CustomTypes Write-Debug "Request BODY: $methodCall" } try { $client = New-Object Net.WebClient $client.Encoding = [System.Text.Encoding]::UTF8 $response = $client.UploadString($Url, $methodCall) $doc = New-Object Xml.XmlDocument $doc.LoadXml($response) [Xml.XmlDocument]$doc } catch [System.Net.WebException],[System.IO.IOException]{ $message = "WebClient Error" $itemNotFoundException = New-Object -TypeName System.Management.Automation.ItemNotFoundException -ArgumentList $message $errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $itemNotFoundException,$itemNotFoundException.GetType().Name,'ConnectionError',$client Throw $errorRecord } catch { Throw $_ } } End {} } function ConvertFrom-Xml { [CmdletBinding()] param( # Array node [Parameter(Mandatory = $true)] [System.Xml.XmlDocument]$InputObject ) Begin { $endFormats = @('Int32','Double','Boolean','String','False','dateTime.iso8601') function ConvertFrom-XmlNode { [CmdletBinding()] [OutputType( [System.Int32], [System.Double], [System.Boolean], [System.String], [System.Boolean], [System.Datetime] )] param( [Parameter(Mandatory = $true)] $InputNode ) Begin { $endFormats = @('Int32','Double','Boolean','String','False','dateTime.iso8601') } Process { switch (($InputNode | Get-Member -MemberType Properties).Name) { 'struct' { $properties = @{} foreach ($member in ($InputNode.struct.member)) { if (!($member.value.gettype().name -in ("XmlElement","Object[]"))) { $properties[$member.name] = $member.value } else { $properties[$member.name] = ConvertFrom-XmlNode $member.value } } $properties break } 'array' { if ($InputNode.array.data) { foreach ($member in ($InputNode.array.data)) { if (!($member.value.gettype().name -in ("XmlElement","Object[]"))) { $member.value } else { $member.value | ForEach-Object { ConvertFrom-XmlNode $_ } } } } break } 'boolean' { [bool]$InputNode.boolean break } 'dateTime.iso8601' { $string = $InputNode.'dateTime.iso8601' [datetime]::ParseExact($string,"yyyyMMddTHH:mm:ss",$null) break } Default { $InputNode break } } } End {} } } Process { foreach ($param in ($InputObject.methodResponse.params.param)) { foreach ($value in $param.value) { ConvertFrom-XmlNode $value } } } End {} } |