Public/ps1/Html/Start-ApprxrJobHtml.ps1
|
<#
.SYNOPSIS Executes an HTTP job for Apprxr, handling authentication, headers, request/response encoding, and structured error handling. .DESCRIPTION Sends an HTTP request (GET or POST) to a specified URL with provided headers, body, and authentication. Handles authentication via Get-ApprxrAuthenticationRoute, logs all request and response details (optionally to HTML log), and returns a structured result. Handles Unicode decoding, ensures UTF-8 encoding for outgoing bodies, and extracts detailed error information from the response. Compatible with both Windows PowerShell and PowerShell Core. .PARAMETER taskInformation Hashtable or object containing the job parameters: - url: Target endpoint (required) - httpMethod: GET, POST, etc. (optional, defaults based on body) - headers: Hashtable of HTTP headers (optional) - body: Request body (string, hashtable, or PSCustomObject) - contentType: Content-Type header (optional, defaults to application/json; charset=utf-8) - channelId, id, type: Used for authentication lookup (optional) .PARAMETER debugMode If specified, enables debug mode which logs all request/response details using Log-JobHtml. Can also be controlled via ApprxrHtmlDebugMode configuration setting. .EXAMPLE Start-ApprxrJobHtml -taskInformation @{ url = 'https://example.com/api'; httpMethod = 'POST'; headers = @{ Authorization = 'Bearer ...' }; body = $jsonBody; contentType = 'application/json' } .EXAMPLE Start-ApprxrJobHtml -taskInformation $taskInfo -debugMode .NOTES - Used for running HTTP-based jobs in Apprxr automation, including REST API calls with authentication and custom headers. - Ensures outgoing body is UTF-8 encoded and Content-Type includes charset=utf-8. - Handles Unicode escape decoding in both body and URL. - Logs to JobHtml.log if Log-JobHtml is available and debug mode is enabled. - Returns a hashtable with keys: message, success, and optionally messageCode. #> function Start-ApprxrJobHtml { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [hashtable]$taskInformation, [switch]$debugMode ) # Check if debug mode is enabled if (-not $debugMode) { try { $debugConfig = Get-ApprxrConfigurationValue -name "ApprxrHtmlDebugMode" -ErrorAction SilentlyContinue if ($debugConfig -and ($debugConfig -eq $true -or $debugConfig -eq "true" -or $debugConfig -eq "1")) { $debugMode = $true } } catch { # Configuration value not found, continue without debug mode } } # Unicode decoding and JSON conversion for body if ($taskInformation.body) { if ($taskInformation.body -is [PSCustomObject]) { $taskInformation.body = [string]($taskInformation.body | ConvertTo-Json -Depth 10 -Compress) Log-JobHtml "... Body converted to JSON string from PSCustomObject." -debugMode:$debugMode } # Fix for PowerShell 5.1: use double quotes and double backslash in regex $taskInformation.body = $taskInformation.body -replace "\\u", "\u" $taskInformation.body = [regex]::Replace($taskInformation.body, "\\u([0-9A-Fa-f]{4})", { param($m) [char]([convert]::ToInt32($m.Groups[1].Value,16)) }) } # Log all input parameters if debug mode is enabled Log-JobHtml "Headers: $($taskInformation.headers | Out-String)" -debugMode:$debugMode Log-JobHtml "Body: $($taskInformation.body | Out-String)" -debugMode:$debugMode Log-JobHtml "Method: $($taskInformation.httpMethod | Out-String)" -debugMode:$debugMode Log-JobHtml "Url: $($taskInformation.url | Out-String)" -debugMode:$debugMode Log-JobHtml "ContentType: $($taskInformation.contentType | Out-String)" -debugMode:$debugMode if (-not $taskInformation.url) { return @{ message = "No url found" success = $false } } try { # Populate authentication info using Get-ApprxrAuthenticationRoute $authInfo = Get-ApprxrAuthenticationRoute -hostURI $taskInformation.url -channelId $taskInformation.channelId -id $taskInformation.id -type $taskInformation.type if ($authInfo) { if ($debugMode) { Log-JobHtml ("Authentication Info: $($authInfo | ConvertTo-Json -Depth 10)") -debugMode:$debugMode } } } catch { Log ("Error setting authentication: $($_.Exception.Message)") return @{ message = "Error setting authentication: $($_.Exception.Message)" success = $false } } $result = @{} $headers = @{} # Handle null or empty headers if ($taskInformation.headers) { $taskInformation.headers.psobject.Properties | ForEach-Object { $headers[$_.Name] = $_.Value } } try { # Decode any Unicode escape sequences in the URL (e.g., \u0026 for &) $decodedUrl = $taskInformation.url -replace '\\u([0-9A-Fa-f]{4})', { [char]([convert]::ToInt32($args[0],16)) } Log-JobHtml ("Decoded URL: $decodedUrl") -debugMode:$debugMode $invokeParams = @{ Uri = $decodedUrl Method = $taskInformation.httpMethod Headers = $headers ErrorAction = 'Stop' } $isCore = $PSVersionTable.PSEdition -eq 'Core' # Add authentication/credential if available if ($authInfo) { # NTLM/Negotiate: Set Credential always, set Authentication only in PowerShell Core if ($authInfo.Authentication -eq 'Negotiate' -or $authInfo.Authentication -eq 'NTLM') { if ($authInfo.PSObject.Properties['username'] -and $authInfo.PSObject.Properties['password'] -and $authInfo.username -and $authInfo.password) { try { $securePassword = ConvertTo-SecureString -String $authInfo.password -AsPlainText -Force $credential = New-Object System.Management.Automation.PSCredential (($authInfo.username -replace '\\\\','\\'), $securePassword) $invokeParams.Credential = $credential Log-JobHtml ("Adding Credential for user: $($authInfo.username)") -debugMode:$debugMode } catch { Log-JobHtml ("Error creating or assigning credential: $($_.Exception.Message)") -debugMode:$debugMode throw $_ } } $invokeParams | Add-Member -MemberType NoteProperty -Name "Authentication" -Value "Negotiate" Log-JobHtml ("Credential and authentication assignment complete. Preparing to invoke web request.") -debugMode:$debugMode } elseif ($authInfo.Authentication -eq 'Basic') { # For Basic, add Authorization header manually if ($authInfo.PSObject.Properties['username'] -and $authInfo.PSObject.Properties['password'] -and $authInfo.username -and $authInfo.password) { $pair = "$($authInfo.username):$($authInfo.password)" $bytes = [System.Text.Encoding]::UTF8.GetBytes($pair) $base64 = [Convert]::ToBase64String($bytes) $headers['Authorization'] = "Basic $base64" Log-JobHtml ("Adding Basic Authorization header for user: $($authInfo.username)") -debugMode:$debugMode } if ($isCore -and $authInfo.PSObject.Properties['Authentication'] -and $authInfo.Authentication) { $invokeParams.Authentication = $authInfo.Authentication Log-JobHtml ("Adding Authentication (Core): $($authInfo.Authentication)") -debugMode:$debugMode } } } if ($taskInformation.body) { $bodyToSend = $taskInformation.body # Set default method to POST if body is present and no method specified if (-not $taskInformation.httpMethod) { $taskInformation.httpMethod = "POST" } # Always set Content-Type to include charset=utf-8 if (-not $taskInformation.contentType) { $taskInformation.contentType = "application/json" } if ($taskInformation.contentType -notmatch "charset") { $taskInformation.contentType = $taskInformation.contentType.TrimEnd(';') + "; charset=utf-8" } Log-JobHtml ("Setting Content-Type to: $($taskInformation.contentType)") -debugMode:$debugMode $invokeParams.ContentType = $taskInformation.contentType # If body is an object and content type is application/json, serialize to JSON if ($taskInformation.contentType -like 'application/json*' -and ($bodyToSend -is [PSCustomObject] -or $bodyToSend -is [hashtable])) { Log-JobHtml ("Converting body object to JSON string.") $bodyToSend = $bodyToSend | ConvertTo-Json -Depth 10 } else { Log-JobHtml ("Using body as provided. Because content type is not application/json $($taskInformation.contentType) or body is not an ps object $($bodyToSend -is [PSCustomObject]) neither a hashtable $($bodyToSend -is [hashtable]).") } # Ensure UTF-8 encoding if body is a string (send as string, not byte array) if ($bodyToSend -is [string]) { Log-JobHtml ("Ensuring UTF-8 encoding for request body.") $utf8NoBom = New-Object System.Text.UTF8Encoding($false) $bytes = $utf8NoBom.GetBytes($bodyToSend) $utf8Body = $utf8NoBom.GetString($bytes) $invokeParams.Body = $utf8Body Log-JobHtml ("Outgoing Body (UTF-8): $utf8Body") } else { $invokeParams.Body = $bodyToSend Log-JobHtml ("Outgoing Body (non-string): $($bodyToSend | Out-String)") } } else { if (-not $taskInformation.httpMethod) { $taskInformation.httpMethod = "GET" } if ($taskInformation.body) { $bodyToSend = $taskInformation.body # If body is an object and content type is application/json, serialize to JSON if ($taskInformation.contentType -eq 'application/json' -and ($bodyToSend -is [PSCustomObject] -or $bodyToSend -is [hashtable])) { $bodyToSend = $bodyToSend | ConvertTo-Json -Depth 10 } $invokeParams.Body = $bodyToSend $invokeParams.ContentType = $taskInformation.contentType } } Log-JobHtml ("Send Invoke-WebRequest Parameters: $($invokeParams | ConvertTo-Json -Depth 10)") if ($isCore) { $response = Invoke-WebRequest @invokeParams } else { $response = Invoke-WebRequest @invokeParams -UseBasicParsing } Log-JobHtml ("Invoke-WebRequest completed.") $cookiesObj = Get-CookiesObject $response.Cookies Log-JobHtml ("Cookies Object: $($cookiesObj | ConvertTo-Json -Depth 10)") if ($null -eq $cookiesObj) { $cookiesObj = @() } $headersObj = @{} if ($response.PSObject.Properties['Headers']) { foreach ($key in $response.Headers.Keys) { $headersObj[$key] = $response.Headers[$key] } } elseif ($response.PSObject.Properties['BaseResponse'] -and $response.BaseResponse.Headers) { foreach ($key in $response.BaseResponse.Headers.Keys) { $headersObj[$key] = $response.BaseResponse.Headers[$key] } } $resultObj = @{} if ($response.PSObject.Properties["StatusCode"]) { $resultObj.StatusCode = $response.StatusCode } if ($response.PSObject.Properties["StatusDescription"]) { $resultObj.StatusDescription = $response.StatusDescription } $resultObj.Headers = $headersObj $resultObj.Cookies = $cookiesObj if ($response.PSObject.Properties["Content"]) { $resultObj.Content = $response.Content } else { $resultObj.Content = $response } if ($response.PSObject.Properties["RawContent"]) { $resultObj.RawContent = $response.RawContent } if ($response.PSObject.Properties["RawContentLength"]) { $resultObj.RawContentLength = $response.RawContentLength } if ($response.PSObject.Properties["BaseResponse"]) { $resultObj.BaseResponse = $response.BaseResponse } Log-JobHtml ("Response: $($resultObj | ConvertTo-Json -Depth 100)") $returnObject = @{ message = $resultObj | ConvertTo-Json -Depth 100 success = $true } Log-JobHtml ("DEBUG MODE - Full Result Object: $($returnObject | ConvertTo-Json -Depth 100)") return $returnObject } catch { $headersObj = @{} $cookiesObj = @() $errorObj = @{} $errorContent = $null if ($_.Exception.Response) { $resp = $_.Exception.Response # Try to extract the raw response body (the red text) try { $reader = [System.IO.StreamReader]::new($resp.GetResponseStream()) $errorContent = $reader.ReadToEnd() $reader.Close() } catch {} if ($resp.Headers) { foreach ($key in $resp.Headers.Keys) { $headersObj[$key] = $resp.Headers[$key] } } $cookiesObj = Get-CookiesObject $resp.Cookies $errorObj.Headers = $headersObj $errorObj.Cookies = $cookiesObj if ($resp.StatusCode) { $errorObj.StatusCode = $resp.StatusCode } if ($resp.StatusDescription) { $errorObj.StatusDescription = $resp.StatusDescription } if ($resp.Content) { $errorObj.Content = $resp.Content } if ($resp.RawContent) { $errorObj.RawContent = $resp.RawContent } if ($resp.RawContentLength) { $errorObj.RawContentLength = $resp.RawContentLength } if ($resp.BaseResponse) { $errorObj.BaseResponse = $resp.BaseResponse } if ($_.Exception.Message) { $errorObj.Message = $_.Exception.Message } if ($errorContent) { $errorObj.ErrorContent = $errorContent } } else { if ($_.Exception.Message) { $errorObj.Message = $_.Exception.Message } } if ($null -eq $errorObj -or $errorObj.Count -eq 0) { Log ("Exception: $($_.Exception.Message)") } else { Log ("Error: $($errorObj | ConvertTo-Json -Depth 100)") } $returnObject = @{ message = if ($null -eq $errorObj -or $errorObj.Count -eq 0) { $_.Exception.Message } else { $errorObj | ConvertTo-Json -Depth 100 } messageCode = if ($_.Exception.Response) { $_.Exception.Response.StatusCode.value__ } else { $null } success = $false } if ($debugMode) { Log ("DEBUG MODE - Full Error Object: $($returnObject | ConvertTo-Json -Depth 100)") } return $returnObject } } # SIG # Begin signature block # MIInKgYJKoZIhvcNAQcCoIInGzCCJxcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBSWTNAZeq09fCg # jeYlii4mKr6z9A5OGwWnE04RzjUdf6CCIKYwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggaXMIIEf6ADAgECAhAfsijAKebq6quDMIHCRvIfMA0GCSqG # SIb3DQEBCwUAMGoxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBT # eXN0ZW1zIFMuQS4xODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9u # IENvZGUgU2lnbmluZyAyMDIxIENBMB4XDTI2MDIwMzEzNTE0NFoXDTI4MDIwMzEz # NTE0M1owgaExHTAbBgNVBA8TFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB # BAGCNzwCAQMTAk5MMREwDwYDVQQFEwg5NjMyNDU2MjELMAkGA1UEBhMCTkwxEDAO # BgNVBAgMB1V0cmVjaHQxEzARBgNVBAcMCkFtZXJzZm9vcnQxETAPBgNVBAoMCEFQ # UFJYIEJWMREwDwYDVQQDDAhBUFBSWCBCVjCCAaIwDQYJKoZIhvcNAQEBBQADggGP # ADCCAYoCggGBAOF+mHloTjmkknba8mOejw0Gq3Aczy1dtatfdLM70ken0n07zIlY # 2jNmyTv5I5RixXAt3LJUwWnUQH8okGFJYvnLZRPyjpCxnEqq8GL6puhKjIIcTs8W # VhoADX+mFtLXygLrfk1j6A+dnNXjoCQMsVSEYppSGrpMpKsPQONb1mmFRx66vqro # HcP93qWYTm/f3TpRzlscIxcQ/3Ab+KLeW0sHgC/a8MVHLicbdUe/pka4jMM98Djj # BtkBc18dI6ECtrDfB2FXXFomvgm5UyPaf2sYxMPL+X/iCNhaQevoT3vttxMvKzAD # JxJK7VsV2nrCvxfTaP4cVd9FfoxUqjCE0eA/IH3qrKqAv3eONjMrRzoO9jJumusi # t91wYT1dwTe/o2FAwfmZ7qxofCXTlO008weUDY5UBw8d+BavVvnDNR08eEcCJN37 # wBJn1hhjFZZD6OxVcMEoyhIG5tJqvmBPyR5A1oYeGSL+/xBbLpAQR0oAhAivxvn4 # POX2r4M/wXGJewIDAQABo4IBfzCCAXswDAYDVR0TAQH/BAIwADBBBgNVHR8EOjA4 # MDagNKAyhjBodHRwOi8vY2V2Y3NjYTIwMjEuY3JsLmNlcnR1bS5wbC9jZXZjc2Nh # MjAyMS5jcmwwdwYIKwYBBQUHAQEEazBpMC4GCCsGAQUFBzABhiJodHRwOi8vY2V2 # Y3NjYTIwMjEub2NzcC1jZXJ0dW0uY29tMDcGCCsGAQUFBzAChitodHRwOi8vcmVw # b3NpdG9yeS5jZXJ0dW0ucGwvY2V2Y3NjYTIwMjEuY2VyMB8GA1UdIwQYMBaAFKxX # yggW3D/FMRwKTdv78d6ZJy00MB0GA1UdDgQWBBSEZ5pbrzKazBz6ZCRYAbuQVlDy # pDBKBgNVHSAEQzBBMAcGBWeBDAEDMDYGCyqEaAGG9ncCBQEHMCcwJQYIKwYBBQUH # AgEWGWh0dHBzOi8vd3d3LmNlcnR1bS5wbC9DUFMwEwYDVR0lBAwwCgYIKwYBBQUH # AwMwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQARtX2dHRUVP9uH # 5FuVIvj22UsNMW2gsexnRxeL/xKxY4BXv6UkQmcgABKbYlWGH0wodaKRhrT9NyK1 # tt0P1C53zAkaiwNU7gSge7s/OPk2yA7g++iVBUDXRelZVCWi31V8yRVV+zXAFEkK # timx00QcEhLFu1DAyjABmKouChHJyqF4dHWytvQZQHw1dfG1OSh6F82thRipW/q1 # ggWzm91QdieGlaSBZy9N881+h4V23R7oJVyWcCUtar19rBJH+/TaZHMo3VDVazhr # CYUKMf/f6RA+iuSsPcpwMi7BZzTxawQtEe/1vG3RLVP94bWl69gY8LiTe0OU23tp # 1PuTiHFLEXjTIb9WnBljH2PvUpJVaxQAMZrunKZvuafYSAvcEfSiRvgFg3r1UH9I # nLjJVxsjfxjnbg5y/5SChh+OBbld7+NdvWVoFUGwqK2IuALD0O2/gtQBS/+UZuiB # GsWpTv9PWI9/C2kPjEcBjC3sZedZOjkEJNSb8siUDItoswA+ddZRBBLdkNJCIKX4 # +lrKAAXdOqGu1QSu57G/OOLyjeUHEWx73XzoPMdgGfkcbdSBQoWBevDbC2xLzqr6 # EQZhplxGR39HZ4M4GyNFcpL4Xhuw1Gma8ldGTeuNw5ktP5Jmf9zvrjP5epHgMl6r # jO0VG4wGU+R4e/FPQClKsyxI/cTJMjCCBrQwggScoAMCAQICEA3HrFcF/yGZLkBD # Igw6SYYwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp # Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMY # RGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTI1MDUwNzAwMDAwMFoXDTM4MDEx # NDIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu # MUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0 # MDk2IFNIQTI1NiAyMDI1IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBALR4MdMKmEFyvjxGwBysddujRmh0tFEXnU2tjQ2UtZmWgyxU7UNqEY81FzJs # Qqr5G7A6c+Gh/qm8Xi4aPCOo2N8S9SLrC6Kbltqn7SWCWgzbNfiR+2fkHUiljNOq # nIVD/gG3SYDEAd4dg2dDGpeZGKe+42DFUF0mR/vtLa4+gKPsYfwEu7EEbkC9+0F2 # w4QJLVSTEG8yAR2CQWIM1iI5PHg62IVwxKSpO0XaF9DPfNBKS7Zazch8NF5vp7ea # Z2CVNxpqumzTCNSOxm+SAWSuIr21Qomb+zzQWKhxKTVVgtmUPAW35xUUFREmDrMx # SNlr/NsJyUXzdtFUUt4aS4CEeIY8y9IaaGBpPNXKFifinT7zL2gdFpBP9qh8SdLn # Eut/GcalNeJQ55IuwnKCgs+nrpuQNfVmUB5KlCX3ZA4x5HHKS+rqBvKWxdCyQEEG # cbLe1b8Aw4wJkhU1JrPsFfxW1gaou30yZ46t4Y9F20HHfIY4/6vHespYMQmUiote # 8ladjS/nJ0+k6MvqzfpzPDOy5y6gqztiT96Fv/9bH7mQyogxG9QEPHrPV6/7umw0 # 52AkyiLA6tQbZl1KhBtTasySkuJDpsZGKdlsjg4u70EwgWbVRSX1Wd4+zoFpp4Ra # +MlKM2baoD6x0VR4RjSpWM8o5a6D8bpfm4CLKczsG7ZrIGNTAgMBAAGjggFdMIIB # WTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTvb1NK6eQGfHrK4pBW9i/U # SezLTjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8E # BAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsG # AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0 # dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQu # Y3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsG # CWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAF877FoAc/gc9EXZxML2+C8i1 # NKZ/zdCHxYgaMH9Pw5tcBnPw6O6FTGNpoV2V4wzSUGvI9NAzaoQk97frPBtIj+ZL # zdp+yXdhOP4hCFATuNT+ReOPK0mCefSG+tXqGpYZ3essBS3q8nL2UwM+NMvEuBd/ # 2vmdYxDCvwzJv2sRUoKEfJ+nN57mQfQXwcAEGCvRR2qKtntujB71WPYAgwPyWLKu # 6RnaID/B0ba2H3LUiwDRAXx1Neq9ydOal95CHfmTnM4I+ZI2rVQfjXQA1WSjjf4J # 2a7jLzWGNqNX+DF0SQzHU0pTi4dBwp9nEC8EAqoxW6q17r0z0noDjs6+BFo+z7bK # SBwZXTRNivYuve3L2oiKNqetRHdqfMTCW/NmKLJ9M+MtucVGyOxiDf06VXxyKkOi # rv6o02OoXN4bFzK0vlNMsvhlqgF2puE6FndlENSmE+9JGYxOGLS/D284NHNboDGc # mWXfwXRy4kbu4QFhOm0xJuF2EZAOk5eCkhSxZON3rGlHqhpB/8MluDezooIs8CVn # rpHMiD2wL40mm53+/j7tFaxYKIqL0Q4ssd8xHZnIn/7GELH3IdvG2XlM9q7WP/Uw # gOkw/HQtyRN62JK4S1C8uw3PdBunvAZapsiI5YKdvlarEvf8EA+8hcpSM9LHJmyr # xaFtoza2zNaQ9k+5t1wwggbNMIIEtaADAgECAhEAu/DMtbe4Mf0hrjJ3iuQMiTAN # BgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8g # VGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9u # IEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAy # MB4XDTIxMDUxOTA1MzIxM1oXDTM2MDUxODA1MzIxM1owajELMAkGA1UEBhMCUEwx # ITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjE4MDYGA1UEAxMvQ2Vy # dHVtIEV4dGVuZGVkIFZhbGlkYXRpb24gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCUvvjwlqjf9eedRTK+NoavZlVx # mQTPIN1nK5XQRUQWyPWwHD54wa2aD2kRDrAjBf71/T0XN9Oqms5ML/Jolgryv3JW # 7/yahp7lg/wn3PDnNwzC1BlFMyCdEktQmUXMmE4mmxSutRY1OGGagENWPjbQ1R/x # 4HuTByWbG+TUV7dIPxE7TMy9Npco4f09DN6QFRXdk7bZTu58kxu6MFH6mzQkC3IX # vExfo0c/CT4/hTZD2XknHjXLzi9jzInSHS9GQqeXd6nWbkwEu7znKpNEcSElorDS # EcRhc1OSOLjUK0x0cOw8whzQ0fWGkPoqgCslbjZoko7U7kj/Fn4TWQ8s75LHjhVa # o+w2SWMP0ELWYIkq1ComGq3wqBD7VU6rtCDoUf237HkgRBxLXFjwp444oKWnchYM # Z7epWX8jqntSr4QHeidL72c6XswQIIZsFglV4BO28jO02yS7w4iyY0c6PQs34GZ7 # SWphsN1DaiX9ysc6hovVYoVA94t+3p4TI1RtK9Ayh0atmfYPJqtBfk5kJ1h4nNax # 0lZmJL9chJjZQb+7XW8MQgstiQdsegaz2QL6Edm+94N8IW6TYaalyO9LeR35A9Mo # TLo79rdB04naTqWdPTBKh7DpS53ZAQn5dYs2per+m7ZeIxnEWaNcG+PbRh+ZslFb # 6M75JgN43szB+/VYVQIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV # HQ4EFgQUrFfKCBbcP8UxHApN2/vx3pknLTQwHwYDVR0jBBgwFoAUtqFUOQLDoD+O # irz61PgcptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMD # MDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5j # cmwwbAYIKwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2Nz # cC1jZXJ0dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0 # dW0ucGwvY3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcC # ARYYaHR0cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC7 # khlZjFbU+gsRdcraFFJKj1PL63MVudnm/AILuNaxm7LIZOYtrZaRWEpR0qd3uhSp # UEI29fMBZvuu3CHXJsM58hbJFo4YoHnyDJgTkeVmc10fd1NI//cI3nRD1/Alx09y # WhPZlze0A/Bvwc3LcZtTywfsL5tD3QAL8ZqelrHt1Eb8AnDEnAzKNbm1mwYmD/5d # y1XkIKfsM1pmRM2ZDcJ4qpwVewE7g+4e+Y+iq42LuaHPvNAkwq0daA9Hp3qemk0c # RFfM5257MvEsu+6w8+44mpQUOgpASVIjY3l2vdf6BUfBJfsd3pcp/2hfDnXaql9A # oB7ps7eCZb9VXKm/u2lTRMf5esOcZFy4udsnGBaOwSSUe0chYusfJ9IjsmpoMl4C # W5E0R/U55Bz6TpDo+gaXQJL4i/zv0UR5papGsDguBASp9g/OfnvGTeWrS9l5orEk # tu9ExrFfPvAKxZK+4AO9bapPhMATraKo6kx6K51pS1ZO2pf1r3B+lXN5bTLgp8g/ # dygUZm2cmauBAzJA5v/iHOb0b4b4Muqc8dxgcX1jZRLtLvxnAWxkKIMfj7A4M0Jr # N4rLBUpPFM0+Bp2sR7rtFqNZW56huaDkxO75eNRN2z4A959R5GBRnFWtgE65+B1R # HAQQ/TOF4mU0Y3oIhVQr3rUh4tdSvIxr8QpsDKIvsDCCBu0wggTVoAMCAQICEAqA # 7xhLjfEFgtHEdqeVdGgwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAV # BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVk # IEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2 # MDQwMDAwMDBaFw0zNjA5MDMyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK # Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQw # OTYgVGltZXN0YW1wIFJlc3BvbmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDQRqwtEsae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ # 3xTWcfsLwOvRxUwXcGx8AUjni6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqV # Q+3bzWYesFtkepErvUSbf+EIYLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjo # T1FpS54dNApZfKY61HAldytxNM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R # 0V3Zp3DjjANwqAf4lEkTlCDQ0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6Un # bksIcFJqLbkIXIPbcNmA98Oskkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39 # iTTFS+ENTqW8m6THuOmHHjQNC3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0 # dVVZw7knh1WZXOLHgDvundrAtuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6ll # N3QgshRta6Eq4B40h5avMcpi54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wg # gn8O2klETsJ7u8xEehGifgJYi+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmS # F3voIgMFtNGh86w3ISHNm0IaadCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwID # AQABo4IBlTCCAZEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85Fx # YxlQQ89hjOgwHwYDVR0jBBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0P # AQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSB # iDCBhTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsG # AQUFBzAChlFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0f # BFgwVjBUoFKgUIZOaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1 # c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1Ud # IAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEA # ZSqt8RwnBLmuYEHs0QhEnmNAciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZ # hY+hIfP2JkQ38U+wtJPBVBajYfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/ # ciZmUnthfAEP1HShTrY+2DE5qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk # 6FxRPyUPxAAYH2Vy1lNM4kzekd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQ # USntbjZ80FU3i54tpx5F/0Kr15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdC # G1+lqvP4FbrQ6IwSBXkZagHLhFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y5 # 8678IgmfORBPC1JKkYaEt2OdDh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V0 # 8X1JUPvB4ILfJdmL+66Gp3CSBXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1q # mcwbdUfcSYCn+OwncVUXf53VJUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfY # xJ7La54i71McVWRP66bW+yERNpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhD # Bf3Froguzzhk++ami+r3Qrx5bIbY3TVzgiFI7Gq3zWcxggXaMIIF1gIBATB+MGox # CzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4x # ODA2BgNVBAMTL0NlcnR1bSBFeHRlbmRlZCBWYWxpZGF0aW9uIENvZGUgU2lnbmlu # ZyAyMDIxIENBAhAfsijAKebq6quDMIHCRvIfMA0GCWCGSAFlAwQCAQUAoIGEMBgG # CisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC # AQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIE # IKJz7MKMKj/CVbX56xq5lwf8x9aGqE9/uIt/lByTnQLJMA0GCSqGSIb3DQEBAQUA # BIIBgAEBveH5BXsV78hjOkKkrtNwE57uoWatyPUwOSXs0r/k63cYFf8kXxCXE8Lb # A1dgfxLkG+GB3qqGuYjsc8ThGkZ73dl8GNAj70SdoTipn8XNEE1ADLeN9NvOX6Vj # 5Q+cXh6hIe/d+onldukGE+Yw0YUWncpojdQi//bBGc8Uam0nSsIhSlitcsZnlncp # FegtcydsF7MBENuTsc7DRspXaCB9JZ4bvEq738CbOWGjd4SjgUum41g9oXp2Wu7j # XIIpVk7wSh0DVNrjjbfLkhkola459CHQtUwNJQvb6TY5XvdGgpjqI4hVsNOPi4nj # 1nTT7iQbccsIJeG6IXhL3V1qmLf5grRyoOzP+3hbcedwPVhOmGKtWe39fcY2eVCQ # Y8bLnA7TbP/05h8gdA8m45V/M1SY0CvL8TSiYePXeF0/3ClcVUPJvfDYUPL0pYnL # 1o1ARgmBpig+AefSnZYLI8b9zfIcgAlxSmjd+98VKQ+DUXDfW4WqX4fQ/SvWYNLw # cMcKZaGCAyYwggMiBgkqhkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYT # AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQg # VHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEC # EAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMx # CwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjAyMDUxNDM5NTRaMC8GCSqG # SIb3DQEJBDEiBCCN7L3RwFdpO1sqVDGSaRrpwUxWtiznQw8S7i5RqKvFejANBgkq # hkiG9w0BAQEFAASCAgCNIyRxE1ksK93Ri7iweHVBTUr0l41P9ZdKDf2Ak3JPpYLX # cSIic+CtWoLtenxvFsNLiMpRIyONRvyRbGoJC0EfPUNuZXduN5xOvvHtyyvP/nUh # ppzEEVRzIuwcO2rHvMqHK84861DoSt3vyK5B44f2IVZ8NY5B6eAWDCQpTzttHQDc # pF213E8rBcgJA1IWaqFRblwcnCSaKGR1DA5xMlJCcasHSL14L9KWkljC8je3zH1f # kbsseKq0ST2D+PFKwaoIzXQjkpt3izEh40nN447PwKFZCggg1R5cnNsLEJRsxOJL # /RLQQrpfqavpJm9eBohJB9ERbHnt4CN+Cn2MOPa6RN94hvceiCd7F+LBl2RBWsn9 # kHPrft02UB0cRvP2FH7aApT4GhMsSy3K55WOWlukLYeJ/NE9wonsbdBjAa2a9JNT # IT6tlSlUJ3NfVE5DUlL0msOfO61DsqteBO+fwygHSH8ZSx9ww9ZH8BdNwicuY1Cf # FHVCB7Gzv2o5GjsAeFE93egrtxx7J4zZVY2HweLEw1c8UikUb2wUpJqL9+d9g8Au # K2KLBYPFCp3xS2cvOc9mS9/SyneUjmMOZsPHhiKFxDakNEljcRbefEEduASt+l4f # TPz9CE29VUtQnkDoawMSRjewMGQy4o49bxp5dJcKcUqnBlj0HRvaZhsVs4HJ0g== # SIG # End signature block |