UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

574 lines (571 loc) 28.7 kB
import { Observable, throwError } from 'rxjs'; import { catchError, filter, mergeMap, take } from 'rxjs/operators'; import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; import { WebsocketStreamConnectionState, WebsocketStreamDataRequestState, WebsocketStreamDataState, WebsocketStreamName, WebsocketStreamProcessor } from './websocket-stream'; /** * The SSH stream provider. */ export var SshProvider; (function (SshProvider) { SshProvider["BeatProvider"] = "beatProvider"; SshProvider["ShellCommandProvider"] = "shellCommandProvider"; SshProvider["FileContentProvider"] = "fileContentProvider"; SshProvider["ProcessStatProvider"] = "processStatProvider"; SshProvider["PerformanceReadingProvider"] = "performanceReadingProvider"; SshProvider["OverviewInfoProvider"] = "overviewInfoProvider"; })(SshProvider || (SshProvider = {})); /** * The SSH content type. */ export var SshContentType; (function (SshContentType) { SshContentType["Text"] = "text"; SshContentType["Error"] = "error"; SshContentType["Json"] = "json"; })(SshContentType || (SshContentType = {})); /** * The SSH stream request type. */ export var SshStreamRequestType; (function (SshStreamRequestType) { SshStreamRequestType["Start"] = "start"; SshStreamRequestType["Update"] = "update"; SshStreamRequestType["Stop"] = "stop"; })(SshStreamRequestType || (SshStreamRequestType = {})); /** * SSH processor interface. Each SSH query creates new observable. */ class SshProcessor extends WebsocketStreamProcessor { streamRequest; dataCount = 0; /** * Initializes a new instance of the SshProcessor class. * @param observer Observer to send back result to caller. * @param target Stream target object. */ constructor(observer, target, streamRequest) { super(observer, target); this.streamRequest = streamRequest; } shouldUpdateRequest() { this.dataCount++; const streamOptions = this.streamRequest.options; // Threshold is 80% of the count. Update the request with existing parameters if update is true. if (!MsftSme.isNullOrUndefined(streamOptions) && streamOptions.count > 0 && this.streamRequest.update && this.dataCount >= streamOptions.count * 0.8) { this.dataCount = 0; return true; } return false; } } /** * The SSH stream class. */ export class SshStream { websocketStream; authorizationManager; static logSourceName = 'SshStream'; static sshAgentVersion = '06132023'; processors = new Map(); strings = MsftSme.getStrings().MsftSmeShell.Core.WebsocketStream.SshStream; /** * Initializes a new instance of the SshStream class. * * @param websocketStream the websocket stream object. * @param authorizationManager the authorization manager object. */ constructor(websocketStream, authorizationManager) { this.websocketStream = websocketStream; this.authorizationManager = authorizationManager; websocketStream.registerProcessor(WebsocketStreamName.SshStreamName, this); } /** * Starts/sends a request to the SSH stream. * * @param nodeName the name of the node to use for this request * @param request The @see SshStreamRequestData to send. * The @see SshStreamOptions.update will determine whether the stream auto-updates. @see SshStream.updateStreamRequest can be used to manually update the stream. * If the stream request is not updated, the stream will end after all data has been emitted. * @return Observable<SshStreamData<T>> the query observable. */ sendStreamRequest(nodeName, request) { const requestState = WebsocketStreamDataRequestState.Normal; return this.createRequest(nodeName, requestState, request); } /** * Updates a previous SSH stream request. * * @param nodeName the name of the node to use for this request * @param request The @see SshStreamRequestData to send. * @return Observable<SshStreamData<T>> the query observable. */ updateStreamRequest(nodeName, request) { const target = this.websocketStream.getTarget(this.authorizationManager, nodeName); const requestState = WebsocketStreamDataRequestState.Normal; const processor = this.processors.get(request.requestId); if (!processor || processor.streamRequest.provider !== request.provider) { Logging.log({ level: LogLevel.Warning, message: this.strings.UpdateError.message, source: SshStream.logSourceName }); return; } const updateRequestData = this.fullRequest(request); const updateRequest = { target, requestState, request: updateRequestData }; this.websocketStream.sendNext(WebsocketStreamName.SshStreamName, updateRequest); } /** * Cancel active SSH query. * Result response comes back to the original query to end. * * @param nodeName the node name. * @param id the id of original request specified as options.queryId. */ cancel(nodeName, requestId, provider, parameters) { const target = this.websocketStream.getTarget(this.authorizationManager, nodeName); const requestState = WebsocketStreamDataRequestState.Cancel; const cancelRequestData = { requestId, provider, parameters, options: { action: SshStreamRequestType.Stop, interval: 0, count: 0 } }; const cancelRequest = { target, requestState, request: this.fullRequest(cancelRequestData) }; this.websocketStream.sendNext(WebsocketStreamName.SshStreamName, cancelRequest); } /** * Reset data for connection cleanup. */ reset() { Logging.log({ level: LogLevel.Warning, message: this.strings.ResetError.message, source: SshStream.logSourceName }); const processors = []; this.processors.forEach((value, key, map) => processors.push(value)); this.processors.clear(); processors.forEach((processor, key, map) => { processor.error(new Error(this.strings.ResetError.message)); }); } /** * Process the socket message. * * @param message the socket message. */ process(message) { if (!message) { throw new Error(this.strings.NoContentError.message); } const innerMessage = JSON.parse(message.response); const processor = this.processors.get(innerMessage.requestId ?? message.id); if (!processor) { Logging.log({ level: LogLevel.Debug, message: this.strings.UnexpectedReceivedError.message, source: SshStream.logSourceName }); return; } switch (message.state) { case WebsocketStreamDataState.Data: this.operationNext(processor, innerMessage); break; case WebsocketStreamDataState.Completed: this.operationComplete(processor, innerMessage); this.operationEnd(innerMessage.requestId); break; case WebsocketStreamDataState.Error: this.operationError(processor, { xhr: message }); this.operationEnd(innerMessage.requestId); break; case WebsocketStreamDataState.Noop: break; } } operationNext(processor, response) { processor.next(response); if (processor.shouldUpdateRequest()) { processor.streamRequest.options.action = SshStreamRequestType.Update; this.updateStreamRequest(processor.target.nodeName, processor.streamRequest); } } operationComplete(processor, response) { processor.next(response); processor.complete(); } operationError(processor, error) { processor.error(error); } operationEnd(id) { this.processors.delete(id); } createRequest(nodeName, requestState, request) { // publish object is created in two ways. // 1) socket is connected so submit the request immediately with simple observable. // (if-block and this is the most of cases.) // 2) socket is not connected so wait for the socket to be ready and submit the request with // complex observable. // (else-block and this is a few cases.) let publish; if (this.websocketStream.socketStateRaw === WebsocketStreamConnectionState.Connected) { publish = this.createRequestSimple(nodeName, requestState, request); } else { publish = this.websocketStream.socketState .pipe(filter(state => state === WebsocketStreamConnectionState.Connected || state === WebsocketStreamConnectionState.Failed || state === WebsocketStreamConnectionState.NotConfigured), take(1), mergeMap(state => { if (state === WebsocketStreamConnectionState.Connected) { return this.createRequestSimple(nodeName, requestState, request); } return throwError(new Error(this.strings.ConnectionError.message)); })); } return publish .pipe(catchError((error, _) => { // retry if reset connection of socket was observed. if (error && error.message === this.strings.ResetError.message) { return this.createRequestSimple(nodeName, requestState, request); } return throwError(error); })); } createRequestSimple(nodeName, requestState, request) { return new Observable(observer => { const target = this.websocketStream.getTarget(this.authorizationManager, nodeName); const id = this.sendRequest(observer, target, requestState, request); return () => { const processor = this.processors.get(id); if (processor) { processor.end = true; if (!processor.closed && !processor.closing) { this.cancel(processor.target.nodeName, id, processor.streamRequest.provider, processor.streamRequest.parameters ?? [""]); } } }; }); } sendRequest(observer, target, requestState, request) { const fullRequest = this.fullRequest(request); const processor = new SshProcessor(observer, target, fullRequest); this.processors.set(fullRequest.requestId, processor); this.websocketStream.sendNext(WebsocketStreamName.SshStreamName, { target, requestState, request: fullRequest }); return fullRequest.requestId; } fullRequest(request) { const requestId = request.requestId ?? MsftSme.getUniqueId(); const fullRequest = { version: SshStream.sshAgentVersion, time: new Date(), requestId, parameters: [""], ...request }; return fullRequest; } } //# sourceMappingURL=ssh-stream.js.map // SIG // Begin signature block // SIG // MIIoNwYJKoZIhvcNAQcCoIIoKDCCKCQCAQExDzANBglg // SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor // SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC // SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg // SIG // xbfUEgW3R8tMBS2iDJNy5EZFosKY5d1OmlyYBhrq3XOg // SIG // gg2FMIIGAzCCA+ugAwIBAgITMwAABAO91ZVdDzsYrQAA // SIG // AAAEAzANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV // SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH // SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv // SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT // SIG // aWduaW5nIFBDQSAyMDExMB4XDTI0MDkxMjIwMTExM1oX // SIG // DTI1MDkxMTIwMTExM1owdDELMAkGA1UEBhMCVVMxEzAR // SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v // SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv // SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA // SIG // n3RnXcCDp20WFMoNNzt4s9fV12T5roRJlv+bshDfvJoM // SIG // ZfhyRnixgUfGAbrRlS1St/EcXFXD2MhRkF3CnMYIoeMO // SIG // MuMyYtxr2sC2B5bDRMUMM/r9I4GP2nowUthCWKFIS1RP // SIG // lM0YoVfKKMaH7bJii29sW+waBUulAKN2c+Gn5znaiOxR // SIG // qIu4OL8f9DCHYpME5+Teek3SL95sH5GQhZq7CqTdM0fB // SIG // w/FmLLx98SpBu7v8XapoTz6jJpyNozhcP/59mi/Fu4tT // SIG // 2rI2vD50Vx/0GlR9DNZ2py/iyPU7DG/3p1n1zluuRp3u // SIG // XKjDfVKH7xDbXcMBJid22a3CPbuC2QJLowIDAQABo4IB // SIG // gjCCAX4wHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB // SIG // BQUHAwMwHQYDVR0OBBYEFOpuKgJKc+OuNYitoqxfHlrE // SIG // gXAZMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQLEyRNaWNy // SIG // b3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQx // SIG // FjAUBgNVBAUTDTIzMDAxMis1MDI5MjYwHwYDVR0jBBgw // SIG // FoAUSG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0w // SIG // SzBJoEegRYZDaHR0cDovL3d3dy5taWNyb3NvZnQuY29t // SIG // L3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx // SIG // LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYB // SIG // BQUHMAKGRWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w // SIG // a2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8yMDEx // SIG // LTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3 // SIG // DQEBCwUAA4ICAQBRaP+hOC1+dSKhbqCr1LIvNEMrRiOQ // SIG // EkPc7D6QWtM+/IbrYiXesNeeCZHCMf3+6xASuDYQ+AyB // SIG // TX0YlXSOxGnBLOzgEukBxezbfnhUTTk7YB2/TxMUcuBC // SIG // P45zMM0CVTaJE8btloB6/3wbFrOhvQHCILx41jTd6kUq // SIG // 4bIBHah3NG0Q1H/FCCwHRGTjAbyiwq5n/pCTxLz5XYCu // SIG // 4RTvy/ZJnFXuuwZynowyju90muegCToTOwpHgE6yRcTv // SIG // Ri16LKCr68Ab8p8QINfFvqWoEwJCXn853rlkpp4k7qzw // SIG // lBNiZ71uw2pbzjQzrRtNbCFQAfmoTtsHFD2tmZvQIg1Q // SIG // VkzM/V1KCjHL54ItqKm7Ay4WyvqWK0VIEaTbdMtbMWbF // SIG // zq2hkRfJTNnFr7RJFeVC/k0DNaab+bpwx5FvCUvkJ3z2 // SIG // wfHWVUckZjEOGmP7cecefrF+rHpif/xW4nJUjMUiPsyD // SIG // btY2Hq3VMLgovj+qe0pkJgpYQzPukPm7RNhbabFNFvq+ // SIG // kXWBX/z/pyuo9qLZfTb697Vi7vll5s/DBjPtfMpyfpWG // SIG // 0phVnAI+0mM4gH09LCMJUERZMgu9bbCGVIQR7cT5YhlL // SIG // t+tpSDtC6XtAzq4PJbKZxFjpB5wk+SRJ1gm87olbfEV9 // SIG // SFdO7iL3jWbjgVi1Qs1iYxBmvh4WhLWr48uouzCCB3ow // SIG // ggVioAMCAQICCmEOkNIAAAAAAAMwDQYJKoZIhvcNAQEL // SIG // BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo // SIG // aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK // SIG // ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMT // SIG // KU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv // SIG // cml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcw // SIG // ODIxMDkwOVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgT // SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc // SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYG // SIG // A1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Eg // SIG // MjAxMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC // SIG // ggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq // SIG // uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJ // SIG // DXlkh36UYCRsr55JnOloXtLfm1OyCizDr9mpK656Ca/X // SIG // llnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3WraPPL // SIG // bfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5 // SIG // pUkp5w2+oBN3vpQ97/vjK1oQH01WKKJ6cuASOrdJXtjt // SIG // 7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7La4zWMW3 // SIG // Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwP // SIG // rRkjhMv0ugOGjfdf8NBSv4yUh7zAIXQlXxgotswnKDgl // SIG // mDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I4iVd0yFLPlLE // SIG // tVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9 // SIG // G9RVS+c5oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/C // SIG // HFfbg43sTUkwp6uO3+xbn6/83bBm4sGXgXvt1u1L50kp // SIG // pxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9A // SIG // N0/B4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE // SIG // 2rCIF96eTvSWsLxGoGyY0uDWiIwLAgMBAAGjggHtMIIB // SIG // 6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k // SIG // 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAwe // SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB // SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h // SIG // 6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDov // SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj // SIG // dHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNy // SIG // bDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 // SIG // dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv // SIG // TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDCB // SIG // nwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYI // SIG // KwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv // SIG // bS9wa2lvcHMvZG9jcy9wcmltYXJ5Y3BzLmh0bTBABggr // SIG // BgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABp // SIG // AGMAeQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkq // SIG // hkiG9w0BAQsFAAOCAgEAZ/KGpZjgVHkaLtPYdGcimwuW // SIG // EeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H // SIG // qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS // SIG // 0LD9a+M+By4pm+Y9G6XUtR13lDni6WTJRD14eiPzE32m // SIG // kHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPyprWElj // SIG // HwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMO // SIG // r5kol5hNDj0L8giJ1h/DMhji8MUtzluetEk5CsYKwsat // SIG // ruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiyWYlobm+n // SIG // t3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVA // SIG // KmWjw11SYobDHWM2l4bf2vP48hahmifhzaWX0O5dY0Hj // SIG // Wwechz4GdwbRBrF1HxS+YWG18NzGGwS+30HHDiju3mUv // SIG // 7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3 // SIG // HjXG0qKin3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs // SIG // 6jeZeRhL/9azI2h15q/6/IvrC4DqaTuv/DDtBEyO3991 // SIG // bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYib // SIG // V3FWTkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0x // SIG // ghoKMIIaBgIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEG // SIG // A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u // SIG // ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n // SIG // IFBDQSAyMDExAhMzAAAEA73VlV0POxitAAAAAAQDMA0G // SIG // CWCGSAFlAwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgor // SIG // BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE // SIG // AYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBkHSJDqjKXNmB2 // SIG // alX6+o6MFfK6VFJxTLMpBZUBmbIDjTBCBgorBgEEAYI3 // SIG // AgEMMTQwMqAUgBIATQBpAGMAcgBvAHMAbwBmAHShGoAY // SIG // aHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3 // SIG // DQEBAQUABIIBAHnXP0KrV2tmqciZHsg6Pu7qa6G/LnjA // SIG // 9uocZGiCnUuNmomBaTqvIkaemKudAKdExaCL5jAs2UyD // SIG // E3aHEa/yb4b7XMurd/tfCWo1dYS3hznEpDAyUVUO/VS0 // SIG // aKtXUBbF80QtXfKQHO9eaaRtZ1ynyS9SqYzyBNyC6yuX // SIG // 7zeSbRLwE0hjSAC3gWHVkKteTaJo+JqUx/TAMDvbNhCK // SIG // EAovfzjyC937dKmnbXtgSMRc3n1Z/P3ejXbd87N0NWTk // SIG // 3QsmUYGahxejmBG8m0zb7mRhR4d2UKXBu6ZMwfX4XgsY // SIG // yOU4DsnYY1oRelb8Ta2NtwKHl02L5sva7OoSvdGR95/s // SIG // H7mhgheUMIIXkAYKKwYBBAGCNwMDATGCF4Awghd8Bgkq // SIG // hkiG9w0BBwKgghdtMIIXaQIBAzEPMA0GCWCGSAFlAwQC // SIG // AQUAMIIBUgYLKoZIhvcNAQkQAQSgggFBBIIBPTCCATkC // SIG // AQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg // SIG // I3jHbxhxwwjE2n1KoowRNjirqPvYUfgAcVhCqIyaQdwC // SIG // BmeuO9psABgTMjAyNTAyMjAxNTI4NDEuNTkxWjAEgAIB // SIG // 9KCB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgT // SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc // SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMG // SIG // A1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9u // SIG // czEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjMzMDMt // SIG // MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt // SIG // ZS1TdGFtcCBTZXJ2aWNloIIR6jCCByAwggUIoAMCAQIC // SIG // EzMAAAHm2UKe6gD4feEAAQAAAeYwDQYJKoZIhvcNAQEL // SIG // BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp // SIG // bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT // SIG // FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd // SIG // TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN // SIG // MjMxMjA2MTg0NTE1WhcNMjUwMzA1MTg0NTE1WjCByzEL // SIG // MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x // SIG // EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv // SIG // c29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z // SIG // b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMe // SIG // blNoaWVsZCBUU1MgRVNOOjMzMDMtMDVFMC1EOTQ3MSUw // SIG // IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2 // SIG // aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC // SIG // AgEAvb6YfOLYJbhM5v8KTSpQI3BJRs35bygA2dQ/tnf4 // SIG // nmGBLRdhyjKyhhQzi6S1lhuQxMoim5WCqxNp7eeNDhrt // SIG // +WcdIFyQRNM1mp2RAIwo7eHhgYvrmpGbJO9Mx00Bx8nz // SIG // /gd5tgUkjRT4YLFSD6er6P/bejnjXsyMF+ROflcBBt+5 // SIG // 2YBHsUBdn0GWG8UNQGrqg70XV7EqStXYdVAbfRGjLM7r // SIG // nGkeZzMEDerA4xvfRc3SvQLc25+EppbKC1LUQIf++vLC // SIG // ndGNYTJilR8CF/P+CblEVAUWdCVrtDWEAafJIZLtfEPA // SIG // gEOdNLRQe1R96Q/M6AOJXAOyZMUxqDyq7n5vpUWQAOIj // SIG // IG3C2dj/8UnZyhcVPLy99UaDZWSYhi+TKk1778gS8/jE // SIG // T+BJ/TcntTfMf5SQ9bLXTaOcCRvpoF7BP8384NhmlakH // SIG // MxR4NDZfG6SKpzRVEXkEatwtY1WDAknHoDcx3mLcOTpm // SIG // f+3lZ0Zo15QrC73bMTs3NWFZ+p2S5EA+ct9R2KwfYiB7 // SIG // rMIWjL9oSTTY1Z3ZKVsvd+DyGblkzJN+tJI1zxcJdlr9 // SIG // U85vbTqwqvPpeNPCiC+ifnn6BjZEfGAdzPrtbWHlQzv0 // SIG // 3Dmxh8WhhQekGcQFKZ3weTfzJgTcTDqsxtTfEIsFvILY // SIG // 4zCYhQX+omGKwo7UcBeyD3UCxbUCAwEAAaOCAUkwggFF // SIG // MB0GA1UdDgQWBBTlBDF57TeePtdWTPkLu0Oa4NxXPTAf // SIG // BgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf // SIG // BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv // SIG // c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU // SIG // aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI // SIG // KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8v // SIG // d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p // SIG // Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEw // SIG // KDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM // SIG // MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkq // SIG // hkiG9w0BAQsFAAOCAgEAYW7qIE2qUbLsEpGhTEOncrvG // SIG // QmFKVkPIiK24uNLVNoSuqR4LtIY9M//cxUUceQ34bpI7 // SIG // 24wP3UuVWH8c9RCGz5bfPezCdrARjtmC2MGHpXyaQ8Gw // SIG // dF0vWZK6qc6ul/5Zg0A59xub3GKWEAieeSy78hZSdfeQ // SIG // FaOFsvFF+ae4PVtzIDR0DKTPhFeuPnLM4+B6OWkJnihr // SIG // Ssu8O9nkWP71g7qba7867hTZigiLddlHAOQTrF6dT7ZI // SIG // 8dskbAo++w0ppdM1WI6lvyElpKxo8nlSfpIc3LcWi5JJ // SIG // VcAsYoKJA+n5Fm8tIQhCkzkzzM4boDyAHMXB7EdrMdNW // SIG // EWvaR9s73XbLgRH0hRugo9EErxGfzPZifoeJomkEkewB // SIG // G1Rg28kSpGJ/NEvtwJkZYd2TnvgRaieezl3XiA0h27x8 // SIG // ye6E6hvPepd3lIT7GYOvXzYMU8Zb0TZkRP/utWI+2dbg // SIG // dF2ED+tK7DC3U5VWBMmVJeTC0y+S76haM2ZUtl6I4uAR // SIG // D+nXVU85hyeKHTmTFk03kNCMJ1hvfL1r/66D3oAq9Rut // SIG // RVa3VyxNwFyH7eGTeGZA056AIT8Ow2TT0ZUluE21Y/y8 // SIG // NF75y2DcDFAPaLmP8MfbXk2ifL05G4GMmjmChc+qzUV2 // SIG // eGn+ntyF8DAn3wmrKSlFwu9mDLuVvC/88k8bDVBIAW0w // SIG // ggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAV // SIG // MA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET // SIG // MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk // SIG // bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 // SIG // aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0 // SIG // aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAx // SIG // ODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYT // SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH // SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y // SIG // cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l // SIG // LVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEF // SIG // AAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7V // SIG // gtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H // SIG // ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKD // SIG // RLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gm // SIG // U3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36M // SIG // EBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+y // SIG // OSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoF // SIG // VZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJi // SIG // ss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGa // SIG // RnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+Autuqfjbs // SIG // Nkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afo // SIG // mXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9 // SIG // ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk // SIG // i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y // SIG // 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV // SIG // 2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0C // SIG // AwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEw // SIG // IwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/ // SIG // LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnp // SIG // cjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8G // SIG // CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j // SIG // b20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYD // SIG // VR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAwe // SIG // CgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB // SIG // /wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9 // SIG // lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov // SIG // L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVj // SIG // dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoG // SIG // CCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov // SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS // SIG // b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcN // SIG // AQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pc // SIG // FLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHk // SIG // wo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF // SIG // vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4 // SIG // U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2 // SIG // EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8Atq // SIG // gcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mj // SIG // dAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZM // SIG // cm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQE // SIG // cb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2f // SIG // pCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBM // SIG // drVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L // SIG // +DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJ // SIG // C4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU // SIG // 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/ // SIG // 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDTTCC // SIG // AjUCAQEwgfmhgdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMw // SIG // EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt // SIG // b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp // SIG // b24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9w // SIG // ZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT // SIG // TjozMzAzLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z // SIG // b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsO // SIG // AwIaAxUA4ljQXhSfY72hKzeuA9RvOni9JruggYMwgYCk // SIG // fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu // SIG // Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV // SIG // TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N // SIG // aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkq // SIG // hkiG9w0BAQsFAAIFAOthS5IwIhgPMjAyNTAyMjAwNjM0 // SIG // NThaGA8yMDI1MDIyMTA2MzQ1OFowdDA6BgorBgEEAYRZ // SIG // CgQBMSwwKjAKAgUA62FLkgIBADAHAgEAAgIivTAHAgEA // SIG // AgISczAKAgUA62KdEgIBADA2BgorBgEEAYRZCgQCMSgw // SIG // JjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB // SIG // AAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAY4mPM5ECZ // SIG // 4XIU6x80yupEs5UMni9qfrC0GLHXbT4Dbf7kMkDgiAjs // SIG // USph+WVwpaLwDlD4sg6IvV1r/y5qRKetMqbHBkOY/V7i // SIG // nuewknxInb5VUQxRjfkREgTFmT2JYKgsz8hqNOwdqdCv // SIG // axgrGNE2fJsOpKYH/bFiafJBSUTNMG7QXASNUR0imca4 // SIG // H0SoBFlZ9j5XrcBs5TVqsR+Fkf1NqPipGNoyzeJdzNaf // SIG // /LbLpC9nunLhnX7oLaXbrJ+kaj8U7qRZLZuDis93o8X2 // SIG // SY9lVAb65ZuHTaO9JU/YXIIKDp1QOfvVmeWg38voWR9P // SIG // /7TsW1B3D6XD/o//u0kndxzRMYIEDTCCBAkCAQEwgZMw // SIG // fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 // SIG // b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p // SIG // Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWlj // SIG // cm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAHm // SIG // 2UKe6gD4feEAAQAAAeYwDQYJYIZIAWUDBAIBBQCgggFK // SIG // MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkq // SIG // hkiG9w0BCQQxIgQg5iYekUfpHyL6mFdJ97iXN4isUtJ+ // SIG // whmkACYx254Ubk8wgfoGCyqGSIb3DQEJEAIvMYHqMIHn // SIG // MIHkMIG9BCDPu6OGqB6zCWhvIJyztateoSGHEZ6MuhZz // SIG // gm50g9LGWTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw // SIG // EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt // SIG // b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp // SIG // b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w // SIG // IFBDQSAyMDEwAhMzAAAB5tlCnuoA+H3hAAEAAAHmMCIE // SIG // IFgJEZSFr0L2nCjzxibA9IwrUUcEKI4qZmlyfbEXDrzD // SIG // MA0GCSqGSIb3DQEBCwUABIICAIsUGMnzsf55Ptegt3V9 // SIG // 61mf8j9w2Ow/w+k82E+E0pzsKsysaXkaQssxN8NV0ZYc // SIG // UddVHNMOTJSrCqi7tJwymvCA0zXAKxxmZK1ho3vphu1B // SIG // eWeAiWK3sMy46cprw+A+hVu9PpGClw+OljOjQX5BnYh8 // SIG // bncu+qYVqifOlMlIrwEponumGDSnHw4p+1dpVZtiZthc // SIG // 0hHQJUwBoOD75H0kW4S+Y/X5F9biVr+EsEVyPOOpg/pF // SIG // EdEdtuTRHRyu2+qA8LReeZ7wOUJwg1j2vyR+MxdYI3KY // SIG // H5v5mp4XEWgubCoW+t4eCV1KtDY417wyi399C/MW0HSY // SIG // FCkmgTGOjBaOMkTSbur0M/Ri6zW1OVIawZ4G/uvfpuh1 // SIG // pQj1va3GX/Mt8t0iGNuIDlpG3oOUoQ7J+KJT2N4SlAtd // SIG // ZRbM49Fa9+GCgAvQwyEDBUq77x/5xE0cKHMrOEJyC4od // SIG // RH1XXaebbrmAKu9we7nAlwkMnRuN+Pg3WvSAX4d5awwj // SIG // wOajLKYPcSBjB3W+n+TWdj7r6/wvlcriho2W+fgHbtd0 // SIG // syjSYbb5HBVxublir2JtJc6yOMOiDkaCEoqoVDESdrtH // SIG // oPfpW0gD8hN5tP80TE6+DISyT1AxScc3wKF0fmfRMOyd // SIG // QpmHcC7kxpkuQ1z96Jnlb+Q5QdI5TEVoC90AmW5nhT9s // SIG // Sna0 // SIG // End signature block