UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

1,034 lines (1,031 loc) 45.1 kB
import { EMPTY, Observable, throwError } from 'rxjs'; import { catchError, expand, mergeMap, tap } from 'rxjs/operators'; import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; import { SmeWebTelemetry } from '../diagnostics/sme-web-telemetry'; import { TelemetryEventStates } from '../diagnostics/sme-web-telemetry-models'; import { Disposer } from './disposable'; import { headerConstants } from './http-constants'; import { Net } from './net'; /** * The PowerShellSession class. */ export class PowerShellSession { powerShell; lifetime; constructor(powerShell, lifetime) { this.powerShell = powerShell; this.lifetime = lifetime; } /** * Gets the node name of session. */ get nodeName() { return this.powerShell.nodeName; } /** * Dispose the session object. */ dispose() { if (this.lifetime) { this.lifetime.dispose(); } } } /** * Class containing methods related to PowerShell runspace creation/deletion/command using PowerShell Raw API plugin. * - It's auto holding the session as long as it's used within last 3 minutes. */ export class PowerShellRaw { nodeConnection; context; // 3 minutes holding time. static maxDeltaTimeInMs = 3 * 60 * 1000; sessionId; timestampInMs = 0; markDelete = false; internalActive = false; cancelPending = false; /** * Initializes a new instance of the PowerShellRaw class. * * @param nodeConnection The node connection service. * @param context The context of PowerShell run. */ constructor(nodeConnection, context) { this.nodeConnection = nodeConnection; this.context = context; } /** * Gets active status of PowerShell execution. */ get active() { return this.internalActive; } /** * Dispose the runspace. */ dispose() { if (!this.active) { // only close sessions that have been created. // If a result was cached a component may not // execute a command and still dispose the session // when the component is destroyed. if (this.sessionId) { this.close(); } } else { this.markDelete = true; } } /** * Runs the given command * * @param command The command to execute. * @param options the powershell options. */ runCommand(command, options) { // take the timestamp only success/healthy case. // error session would be auto-deleted after expiration time. this.internalActive = true; return this.command(command, options) .pipe(catchError((error) => this.fallbackToJea(error, command, options)), expand((data) => { this.timestampInMs = Date.now(); if (this.checkCompleted(data)) { return EMPTY; } if (this.cancelPending) { // submit cancel request. // after set active state to false and complete the observable. this.cancelPending = false; return this.cancel() .pipe(catchError(() => { this.internalActive = false; return EMPTY; }), mergeMap(() => { this.internalActive = false; return EMPTY; })); } const url = Net.powerShellApiRetrieveOutput.format(this.sessionId); return this.nodeConnection.get(this.context.nodeName, url, this.context.requestOptions) .pipe(catchError((error) => { SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: error.response }); return this.fallbackToJea(error, command, options); })); })); } /** * Close/Delete the session / runspace. */ close() { if (this.context.requestOptions.automatic) { return; } if (this.sessionId) { const sessionUri = Net.powerShellApiSessions.format(this.sessionId); this.sessionId = null; this.nodeConnection.deleteQuick(this.context.nodeName, sessionUri, this.context.requestOptions); return; } Logging.log({ level: LogLevel.Verbose, source: 'PowerShell/close', message: MsftSme.getStrings().MsftSmeShell.Core.Error.PowerShellUnableSessionClose.message }); } /** * Cancel the command. */ cancelCommand() { if (this.internalActive) { this.cancelPending = true; } return EMPTY; } /** * Perform the JEA fallback, if applicable. * * @param error The error to handle * @param command The command * @param options The request options */ fallbackToJea(error, command, options) { const authError = Net.isUnauthorized(error) || error.status === 400 /* HttpStatusCode.BadRequest */; const responseEndpoint = error && error.xhr && error.xhr.getResponseHeader(headerConstants.POWERSHELL_ENDPOINT); let requestEndpoint = (options && options.powerShellEndpoint); requestEndpoint = requestEndpoint || (this.context.requestOptions && this.context.requestOptions.powerShellEndpoint); const cancel = error.handlerError && error.handlerError.code && error.handlerError.code === 'ManageAsDialogCancel'; const credSSP = (options && options.authenticationMechanism === 'Credssp'); if (!cancel && authError && responseEndpoint && requestEndpoint !== responseEndpoint && !credSSP) { this.context.requestOptions.powerShellEndpoint = responseEndpoint; return this.command(command, options) .pipe(tap(() => { // The JEA request went through - persist this context in authorization manager. this.nodeConnection.saveJeaContext(this.context.nodeName, responseEndpoint); })); } // close on error if sessionId is available. if (options && options.closeOnError) { if (!this.sessionId) { this.sessionId = error && error.xhr && error.xhr.response && error.xhr.response.sessionId; } if (this.sessionId) { this.close(); } } this.internalActive = false; return throwError(() => error); } cancel() { if (this.sessionId && this.internalActive) { const cancelUri = Net.powerShellApiCancelCommand.format(this.sessionId); return this.nodeConnection.post(this.context.nodeName, cancelUri, null, this.context.requestOptions); } Logging.log({ level: LogLevel.Warning, source: 'PowerShell', message: MsftSme.getStrings().MsftSmeShell.Core.Error.PowerShellUnableCancelCommand.message }); return EMPTY; } /** * Gets if timestamp was expired. */ get _isExpired() { const now = Date.now(); return this.timestampInMs !== 0 && (now - this.timestampInMs) > PowerShellRaw.maxDeltaTimeInMs; } /** * Initiate command execution. It auto recycles old sessions. * * @param command the PowerShell command. */ command(command, options) { const commandPacket = { ...command }; commandPacket.useInProcRunspace = !!options?.useInProcRunspace; const polling = !!this.context.requestOptions.automatic; if (polling) { commandPacket.invokeMode = 'Polling'; } if (options && options.waitTimeMs) { commandPacket.waitTimeMs = options.waitTimeMs; } const data = Net.createPropertiesJSONString(commandPacket); const newOptions = { ...this.context.requestOptions, ...{ logAudit: options && options.logAudit, logTelemetry: options && options.logTelemetry } }; const endpoint = options && options.powerShellEndpoint; if (endpoint) { newOptions.powerShellEndpoint = endpoint; } const token = options && options.authToken; if (token) { newOptions.authToken = token; } const authenticationMechanism = options && options.authenticationMechanism; if (authenticationMechanism) { newOptions.authenticationMechanism = authenticationMechanism; } if (newOptions.logTelemetry && !polling) { SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Started); } let commandResponse; if (polling) { this.sessionId = null; commandResponse = this.nodeConnection.post(this.context.nodeName, Net.powerShellApiInvokeCommand, data, newOptions); } else if (this.sessionId == null || this._isExpired) { this.sessionId = null; const generatedName = this.context.key ? this.context.key + '-newSession' : 'instantSession'; const sessionUri = Net.powerShellApiSessions.format(generatedName); commandResponse = this.nodeConnection.put(this.context.nodeName, sessionUri, data, newOptions); } else { const executeUri = Net.powerShellApiExecuteCommand.format(this.sessionId); commandResponse = this.nodeConnection.post(this.context.nodeName, executeUri, data, newOptions); } return commandResponse.pipe(catchError((error) => { SmeWebTelemetry.tracePowershellEvent(command, TelemetryEventStates.Error, { response: error.response }); return throwError(() => error); })); } checkCompleted(data) { const properties = Net.getItemProperties(data); if (properties.sessionId) { // keep the PS session GUID this.sessionId = properties.sessionId; } if (properties.completed.toLowerCase() === 'true') { this.internalActive = false; if (this.markDelete) { this.close(); } return true; } return false; } } /** * The PowerShell class. * * - Single instance of PowerShell class manages single runspace. * - It queues coming requests and process one at a time sequentially. * - If a command is slow and causing with multiple responses, it aggregates response into single Q result. * - A PowerShell instance should be created through create() function, and it's statically stored/managed into _map collection. * - In QueryCache operation, it can find the PowerShell instance to run PowerShell command by using find() function. * - Once all lifetime references are gone, it deletes the runspace. * - To dispose the PowerShell instance, it can use lifetime.dispose(). */ export class PowerShell { /** * Default PowerShell endpoint. */ static defaultPowerShellEndpoint = 'http://schemas.microsoft.com/powershell/microsoft.powershell'; /** * SME PowerShell endpoint. */ static smePowerShellEndpoint = 'http://schemas.microsoft.com/powershell/microsoft.sme.powershell'; /** * WAC (v2) CredSSP PowerShell endpoint to control client role of CredSSP on the gateway. */ static wacCredSSPEndpoint = 'http://schemas.microsoft.com/powershell/Microsoft.WindowsAdminCenter.Credssp'; /** * Static collection of PowerShell objects. */ static map = {}; /** * Regular expression to match all the occurrences of a single quote */ static escapeRegex = new RegExp('\'', 'g'); /** * The context of PowerShell object. */ context; /** * The queue of PowerShell command requests. */ queue = []; /** * The reference to PowerShellRaw class object. */ raw; /** * Current data to aggregate from multiple data responses. */ currentData; /** * Timestamp when last command started. */ timestamp; /** * Create script as string. * (Notes: Use createCommand() function which is based on PowerShell module, * Update gulpfile.js to generate a PowerShell module to support Show script, JEA and localization.) * * @param resource the script text from legacy ps-code converter. * @param parameters the arguments. * @param flags (optional) the switch flags. */ static createScript(script, parameters, flags) { script = 'function cvt ($o) { return ConvertFrom-Json $o }\n function SmeSubmit {\n' + script + '}\n SmeSubmit'; for (const parameter in parameters) { if (parameters.hasOwnProperty(parameter)) { const value = parameters[parameter]; if (value == null) { script += ' -{0} $null'.format(parameter); } else { script += ' -{0} (cvt \'{1}\')'.format(parameter, JSON.stringify(value).replace(PowerShell.escapeRegex, '\'\'')); } } } if (flags) { for (let i = 0; i < flags.length; i++) { script += ' -{0}'.format(flags[i]); } } return script; } /** * Create PowerShell request command. * (It creates a command object of JEA PowerShell request under restricted user role environment.) * * @param resource the script resource object with command and script data from new ps-code converter. * @param parameters the arguments. * @param flags (optional) the switch flags. * @return PowerShellCommand the PowerShell request command object. */ static createCommand(resource, parameters, flags, resourceName) { // step1: Add Jea prefix dynamically const powerShellPrefix = MsftSme.self().Init.powerShellPrefix; let command = resource.command; if (powerShellPrefix && resource && resource.command) { command = PowerShell.addPowerShellPrefix(powerShellPrefix, resource.command); } // step2: Adding parameters converter from JSON to PowerShell. // step3: Surround full content into SmeSubmit function. let script = 'function cvt($o){return ConvertFrom-Json $o}\n function SmeSubmit{\n' + resource.script + '}\nSmeSubmit'; // step4: Adding localized resources strings overriding Import-LocalizedData as function. if (resourceName) { const strings = MsftSme.getStrings()[resourceName]; if (strings && strings['PowerShell']) { const items = strings['PowerShell']; const keys = Object.keys(items); const rightSingleQuotationMark = '\u2019'; const lines = keys.map(key => (key + '=\'' + items[key] .split(rightSingleQuotationMark) .join('\'\'') .split('\'') .join('\'\'') + '\'\n')); script = 'function Import-LocalizedData{$script:strings=@{\n' + lines.join('') + '}}' + script; } } // step5: Adding each parameter with using converter. for (const parameter in parameters) { if (parameters.hasOwnProperty(parameter)) { const value = parameters[parameter]; if (value == null) { script += ' -{0} $null'.format(parameter); } else { script += ' -{0} (cvt \'{1}\')'.format(parameter, JSON.stringify(value).replace(PowerShell.escapeRegex, '\'\'')); } } } // step6: Adding switch parameters. const flagParameters = {}; if (flags) { for (let i = 0; i < flags.length; i++) { script += ' -{0}'.format(flags[i]); flagParameters[flags[i]] = true; } } return { module: resource.module, command, parameters: { ...flagParameters, ...parameters }, script, state: 'ready' }; } /** * Update the parameters in the PowerShellCommand object, and update the SmeSubmit part of the * script with these new parameters. * * @param command The PowerShellCommand instance to update. * @param parameters The new collection of parameters. * * Note: flags support can be added when it becomes necessary. */ static updateCommandParameters(command, parameters) { for (const parameter in parameters) { if (parameters.hasOwnProperty(parameter)) { command.parameters[parameter] = parameters[parameter]; const value = parameters[parameter]; // Regular expression to capture existing parameter. const regex = new RegExp('(?<=}\\s+SMESubmit .*)-{0} (\\(cvt.+?\\)|\\$null)(?= -|$)'.format(parameter), 'i'); if (command.script.match(regex)) { if (value == null) { command.script = command.script.replace(regex, ' -{0} $null'.format(parameter)); } else { command.script = command.script.replace(regex, ' -{0} (cvt \'{1}\')'.format(parameter, JSON.stringify(value).replace(PowerShell.escapeRegex, '\'\''))); } } else { command.script += ' -{0} (cvt \'{1}\')'.format(parameter, JSON.stringify(value).replace(PowerShell.escapeRegex, '\'\'')); } } } } static create(nodeName, nodeConnection, key, lifetime, requestOptions) { let ps; if (key && lifetime) { ps = PowerShell.map[PowerShell.indexName(nodeName, key)]; if (ps) { ps.addLifetime(lifetime); return ps; } } ps = new PowerShell(nodeName, nodeConnection, key, lifetime, requestOptions); if (key && lifetime) { PowerShell.map[PowerShell.indexName(nodeName, key)] = ps; } return ps; } /** * Find existing PowerShell object. Create call must be called before to create the PowerShell instance. * * @param nodeName The node name. * @param key The shared key to queue the requests to use the single runspace. */ static find(nodeName, key) { return PowerShell.map[PowerShell.indexName(nodeName, key)]; } /** * Gets the command object from string or PowerShellCommand. * * @param scriptOrCommand the script string or PowerShellCommand object. */ static getPowerShellCommand(scriptOrCommand) { return typeof scriptOrCommand === 'string' ? { script: scriptOrCommand, command: null, module: null, state: 'ready' } : { script: scriptOrCommand.script, command: scriptOrCommand.command, module: scriptOrCommand.module || MsftSme.self().Init.powerShellModuleName, parameters: scriptOrCommand.parameters, state: 'ready' }; } /** * Create new options with debugging endpoint if requested. * * @param options the PowerShell session request options. */ static newEndpointOptions(options) { // if there is no endpoint but configured with powerShellEndpoint, set debugging endpoint. const newOptions = { ...(options || {}) }; if (!newOptions.powerShellEndpoint && MsftSme.self().Init.powerShellEndpoint) { newOptions.powerShellEndpoint = MsftSme.self().Init.powerShellEndpoint; } return newOptions; } /** * Create the index name in map collection. * * @param nodeName The node name. * @param key The shared key to queue the requests to use the single runspace. */ static indexName(nodeName, key) { return nodeName + ':' + key; } /** * Adds jea prefix to the command name * * @param jeaPrefix The jea prefix originating from main.ts. * @param command The powershell command to run. */ static addPowerShellPrefix(powerShellPrefix, command) { const hyphenSeparatorIndex = command.indexOf('-'); const verb = command.substring(0, hyphenSeparatorIndex); const target = command.substring(hyphenSeparatorIndex + 1); if (target.indexOf(powerShellPrefix) === 0) { throw new Error('Command already contains prefix'); } return verb + '-' + powerShellPrefix + target; } /** * Initializes a new instance of the PowerShell class. * (private constructor which shouldn't be called directly.) * * @param nodeConnection The node connection service. * @param key The shared key to queue the requests to use the single runspace. * @param lifetime The lifetime container. */ constructor(nodeName, nodeConnection, key, lifetime, options) { this.context = { nodeName: nodeName, key: key, lifetimes: [], requestOptions: PowerShell.newEndpointOptions(options) }; this.timestamp = 0; this.raw = new PowerShellRaw(nodeConnection, this.context); if (key && lifetime) { lifetime.registerForDispose(new Disposer(() => this.lifetimeDisposer(lifetime))); this.context.lifetimes.push(lifetime); } } /** * Gets node name from current context. */ get nodeName() { return this.context.nodeName; } /** * Run PowerShell command. * * @param command The command. * @param options The options. * @return PromiseV The result of PowerShell command. */ run(scriptOrCommand, options) { const command = PowerShell.getPowerShellCommand(scriptOrCommand); if (this.context.lifetimes.length === 0) { // no disposer is assigned, force to close the session after every query. const timeoutMs = options && options.timeoutMs; if (options) { options.timeoutMs = timeoutMs; options.close = true; } else { options = { timeoutMs: timeoutMs, close: true }; } } // queue the request. const observable = this.enqueue(command, options); return observable; } /** * Cancel PowerShell command. */ cancel() { return this.raw.cancelCommand(); } /** * Enqueue a command request. * * @param command The command. * @param options The options. */ enqueue(command, options) { return new Observable((observer) => { this.queue.push({ observer, command, options }); this.dequeue(); }); } /** * Dequeue a command request. */ dequeue() { if (this.raw.active) { return false; } const item = this.queue.shift(); if (item) { this.currentData = null; this.timestamp = Date.now(); this.raw.runCommand(item.command, item.options).subscribe({ next: data => { const properties = Net.getItemProperties(data); this.collect(properties, item.options && item.options.timeoutMs, item.options && item.options.partial ? item.observer : null); }, error: error => { if (item.options && item.options.close) { this.raw.close(); } item.observer.error(error); this.timestamp = 0; this.dequeue(); }, complete: () => { if (item.options && item.options.close) { this.raw.close(); } if (!item.options || !item.options.partial) { item.observer.next(this.currentData); } item.observer.complete(); this.timestamp = 0; this.dequeue(); } }); return true; } return false; } /** * Collect response result and aggregate into single object. * * @param properties The properties of response object. * @param timeoutMs The timeout to cancel command. * @param observer The observer of powershell results. */ collect(properties, timeoutMs, observer) { if (timeoutMs && this.timestamp && (Date.now() - this.timestamp > timeoutMs)) { // force to cancel the command because of unexpected longer execution. this.raw.cancelCommand(); this.timestamp = 0; return; } if (observer) { // return partial data if observer is not null. observer.next(properties); this.currentData = properties; return; } if (this.currentData != null && this.currentData.results && properties.results) { let array; if (MsftSme.getTypeOf(this.currentData.results) === 'array') { array = this.currentData.results; } else { array = [this.currentData.results]; } if (MsftSme.getTypeOf(properties.results) === 'array') { properties.results.forEach((x) => { array.push(x); }); } else { array.push(properties.results); } this.currentData.results = array; return; } this.currentData = properties; } /** * Attach lifetime object to disposer when disposing. * * @param lifetime The lifetime object. */ addLifetime(lifetime) { const found = MsftSme.find(this.context.lifetimes, (value) => value === lifetime); if (!found) { this.context.lifetimes.push(lifetime); lifetime.registerForDispose(new Disposer(() => this.lifetimeDisposer(lifetime))); } } /** * Callback when disposing the container of view model. * If none, reference the PowerShell object. Dispose it. (Delete runspace) * * @param lifetime The lifetime object. */ lifetimeDisposer(lifetime) { const found = MsftSme.find(this.context.lifetimes, (value) => value === lifetime); if (found) { MsftSme.remove(this.context.lifetimes, lifetime); if (this.context.lifetimes.length === 0) { // cancel queue command requests. this.queue.forEach((value) => { value.observer.next(null); value.observer.complete(); }); // delete from the map collection and delete the runspace/session. delete PowerShell.map[PowerShell.indexName(this.context.nodeName, this.context.key)]; this.raw.dispose(); } } } } //# sourceMappingURL=powershell.js.map // SIG // Begin signature block // SIG // MIIoKAYJKoZIhvcNAQcCoIIoGTCCKBUCAQExDzANBglg // SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor // SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC // SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg // SIG // KL/Mg9owo/aysH1dO62lrmCgrM0rkXHg8I26mXvQgt+g // SIG // gg12MIIF9DCCA9ygAwIBAgITMwAABARsdAb/VysncgAA // SIG // AAAEBDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV // SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH // SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv // SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT // SIG // aWduaW5nIFBDQSAyMDExMB4XDTI0MDkxMjIwMTExNFoX // SIG // DTI1MDkxMTIwMTExNFowdDELMAkGA1UEBhMCVVMxEzAR // SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v // SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv // SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA // SIG // tCg32mOdDA6rBBnZSMwxwXegqiDEUFlvQH9Sxww07hY3 // SIG // w7L52tJxLg0mCZjcszQddI6W4NJYb5E9QM319kyyE0l8 // SIG // EvA/pgcxgljDP8E6XIlgVf6W40ms286Cr0azaA1f7vaJ // SIG // jjNhGsMqOSSSXTZDNnfKs5ENG0bkXeB2q5hrp0qLsm/T // SIG // WO3oFjeROZVHN2tgETswHR3WKTm6QjnXgGNj+V6rSZJO // SIG // /WkTqc8NesAo3Up/KjMwgc0e67x9llZLxRyyMWUBE9co // SIG // T2+pUZqYAUDZ84nR1djnMY3PMDYiA84Gw5JpceeED38O // SIG // 0cEIvKdX8uG8oQa047+evMfDRr94MG9EWwIDAQABo4IB // SIG // czCCAW8wHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB // SIG // BQUHAwMwHQYDVR0OBBYEFPIboTWxEw1PmVpZS+AzTDwo // SIG // oxFOMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQLExVNaWNy // SIG // b3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAx // SIG // Mis1MDI5MjMwHwYDVR0jBBgwFoAUSG5k5VAF04KqFzc3 // SIG // IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDov // SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWlj // SIG // Q29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNybDBhBggr // SIG // BgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93 // SIG // d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj // SIG // Q29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNydDAMBgNV // SIG // HRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCI5g/S // SIG // KUFb3wdUHob6Qhnu0Hk0JCkO4925gzI8EqhS+K4umnvS // SIG // BU3acsJ+bJprUiMimA59/5x7WhJ9F9TQYy+aD9AYwMtb // SIG // KsQ/rst+QflfML+Rq8YTAyT/JdkIy7R/1IJUkyIS6srf // SIG // G1AKlX8n6YeAjjEb8MI07wobQp1F1wArgl2B1mpTqHND // SIG // lNqBjfpjySCScWjUHNbIwbDGxiFr93JoEh5AhJqzL+8m // SIG // onaXj7elfsjzIpPnl8NyH2eXjTojYC9a2c4EiX0571Ko // SIG // mhENF3RtR25A7/X7+gk6upuE8tyMy4sBkl2MUSF08U+E // SIG // 2LOVcR8trhYxV1lUi9CdgEU2CxODspdcFwxdT1+G8YNc // SIG // gzHyjx3BNSI4nOZcdSnStUpGhCXbaOIXfvtOSfQX/UwJ // SIG // oruhCugvTnub0Wna6CQiturglCOMyIy/6hu5rMFvqk9A // SIG // ltIJ0fSR5FwljW6PHHDJNbCWrZkaEgIn24M2mG1M/Ppb // SIG // /iF8uRhbgJi5zWxo2nAdyDBqWvpWxYIoee/3yIWpquVY // SIG // cYGhJp/1I1sq/nD4gBVrk1SKX7Do2xAMMO+cFETTNSJq // SIG // fTSSsntTtuBLKRB5mw5qglHKuzapDiiBuD1Zt4QwxA/1 // SIG // kKcyQ5L7uBayG78kxlVNNbyrIOFH3HYmdH0Pv1dIX/Mq // SIG // 7avQpAfIiLpOWwcbjzCCB3owggVioAMCAQICCmEOkNIA // SIG // AAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYT // SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH // SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y // SIG // cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290 // SIG // IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTEx // SIG // MDcwODIwNTkwOVoXDTI2MDcwODIxMDkwOVowfjELMAkG // SIG // A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO // SIG // BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m // SIG // dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0 // SIG // IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZI // SIG // hvcNAQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6q // SIG // ghBNNLrytlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vG // SIG // EtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOlo // SIG // XtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv // SIG // 56sIUM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5k // SIG // NXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ97/vj // SIG // K1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd // SIG // 6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4ftKd // SIG // gCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOGjfdf8NBS // SIG // v4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbs // SIG // YR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43Bd // SIG // D1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5oQ/pI0m8GLhE // SIG // fEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xb // SIG // n6/83bBm4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7Iv // SIG // hNdXnFy/dygo8e1twyiPLI9AN0/B4YVEicQJTMXUpUMv // SIG // dJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY // SIG // 0uDWiIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEE // SIG // AwIBADAdBgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1 // SIG // ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD // SIG // VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j // SIG // BBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0f // SIG // BFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQu // SIG // Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 // SIG // MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRS // SIG // MFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9z // SIG // b2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx // SIG // MV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGXMIGUMIGR // SIG // BgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUHAgEWM2h0dHA6 // SIG // Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9w // SIG // cmltYXJ5Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBM // SIG // AGUAZwBhAGwAXwBwAG8AbABpAGMAeQBfAHMAdABhAHQA // SIG // ZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEA // SIG // Z/KGpZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVf // SIG // Liw++MNy0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQ // SIG // fYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XU // SIG // tR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELuk // SIG // qQUMm+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr // SIG // 3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ1h/D // SIG // Mhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycSca // SIG // f7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6CPxNN // SIG // ZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobDHWM2l4bf // SIG // 2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+ // SIG // YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOX // SIG // pQlLSBCZgB/QACnFsZulP0V3HjXG0qKin3p6IvpIlR+r // SIG // +0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6 // SIG // /IvrC4DqaTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4 // SIG // ETIheu9BCrE/+6jMpF3BoYibV3FWTkhFwELJm3ZbCoBI // SIG // a/15n8G9bW1qyVJzEw16UM0xghoKMIIaBgIBATCBlTB+ // SIG // MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv // SIG // bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj // SIG // cm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNy // SIG // b3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAE // SIG // BGx0Bv9XKydyAAAAAAQEMA0GCWCGSAFlAwQCAQUAoIGu // SIG // MBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG // SIG // AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3 // SIG // DQEJBDEiBCAxwKnfOzYZFcKu34F7hUfF+tja7ji1ucx7 // SIG // TobEoxaPMDBCBgorBgEEAYI3AgEMMTQwMqAUgBIATQBp // SIG // AGMAcgBvAHMAbwBmAHShGoAYaHR0cDovL3d3dy5taWNy // SIG // b3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBAGbhVhcD // SIG // S+lAmDtxuXgk48DYs8Y0F0dVJKSD7xWBiYY+byvKdjIJ // SIG // VmlH0UfEEkImWjK+9/sr+fdAvB68dSsHcHQh5Suu2gg3 // SIG // wTyahSFu97ovE8E9bxlH62z0bhEUMEFtRaoFr84GDQmP // SIG // uZaO+VbFtHNMBgT8mXTecI6jsUZ3osrpSx5UHjCHcmXA // SIG // QZ/rIS0iCWLjKAuHG5gXOSZ8uHO2CnvReaIDv6cg4q7h // SIG // wiUl0jvqBc5RbiLbNcyahiEZgqwwBxsT+QeoSk/7IvSm // SIG // I5RncY5Rg7j4yCL4HQzqZl3YHFaMEKkOYoaLAQ1beqXd // SIG // tUdHN/WUwQn9Wb9673wBExphSDihgheUMIIXkAYKKwYB // SIG // BAGCNwMDATGCF4Awghd8BgkqhkiG9w0BBwKgghdtMIIX // SIG // aQIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUgYLKoZIhvcN // SIG // AQkQAQSgggFBBIIBPTCCATkCAQEGCisGAQQBhFkKAwEw // SIG // MTANBglghkgBZQMEAgEFAAQgU3+ko/YRhs/2SyE/XFr1 // SIG // X+qu6aJuNdWHz4SWod6PjZACBmet2ujCxRgTMjAyNTAy // SIG // MjAxNTI4MzguNDk1WjAEgAIB9KCB0aSBzjCByzELMAkG // SIG // A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO // SIG // BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m // SIG // dCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0 // SIG // IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNo // SIG // aWVsZCBUU1MgRVNOOkE5MzUtMDNFMC1EOTQ3MSUwIwYD // SIG // VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl // SIG // oIIR6jCCByAwggUIoAMCAQICEzMAAAHpD3Ewfl3xEjYA // SIG // AQAAAekwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMC // SIG // VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT // SIG // B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw // SIG // b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt // SIG // U3RhbXAgUENBIDIwMTAwHhcNMjMxMjA2MTg0NTI2WhcN // SIG // MjUwMzA1MTg0NTI2WjCByzELMAkGA1UEBhMCVVMxEzAR // SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v // SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv // SIG // bjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3Bl // SIG // cmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNO // SIG // OkE5MzUtMDNFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3Nv // SIG // ZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG // SIG // 9w0BAQEFAAOCAg8AMIICCgKCAgEArJqMMUEVYKeE0nN5 // SIG // 02usqwDyZ1egO2mWJ08P8sfdLtQ0h/PZ730Dc2/uX5gS // SIG // vKaR++k5ic4x1HCJnfOOQP6b2WOTvDwgbuxqvseV3uqZ // SIG // ULeMcFVFHECE8ZJTmdUZvXyeZ4fIJ8TsWnsxTDONbAyO // SIG // yzKSsCCkDMFw3LWCrwskMupDtrFSwetpBfPdmcHGKYiF // SIG // cdy09Sz3TLdSHkt+SmOTMcpUXU0uxNSaHJd9DYHAYiX6 // SIG // pzHHtOXhIqSLEzuAyJ//07T9Ucee1V37wjvDUgofXcbM // SIG // r54NJVFWPrq6vxvEERaDpf+6DiNEX/EIPt4cmGsh7CPc // SIG // Lbwxxp099Da+Ncc06cNiOmVmiIT8DLuQ73ZBBs1e72E9 // SIG // 7W/bU74mN6bLpdU+Q/d/PwHzS6mp1QibT+Ms9FSQUhlf // SIG // oeumXGlCTsaW0iIyJmjixdfDTo5n9Z8A2rbAaLl1lxSu // SIG // xOUtFS0cqE6gwsRxuJlt5qTUKKTP1NViZ47LFkJbivHm // SIG // /jAypZPRP4TgWCrNin3kOBxu3TnCvsDDmphn8L5CHu3Z // SIG // Mpc5vAXgFEAvC8awEMpIUh8vhWkPdwwJX0GKMGA7cxl6 // SIG // hOsDgE3ihSN9LvWJcQ08wLiwytO93J3TFeKmg93rlwOs // SIG // VDQqM4O64oYh1GjONwJm/RBrkZdNtvsj8HJZspLLJN9G // SIG // uEad7/UCAwEAAaOCAUkwggFFMB0GA1UdDgQWBBSRfjOJ // SIG // xQh2I7iI9Frr/o3I7QfsTjAfBgNVHSMEGDAWgBSfpxVd // SIG // AF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQ // SIG // hk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz // SIG // L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENB // SIG // JTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwG // SIG // CCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5j // SIG // b20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUt // SIG // U3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB // SIG // Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4G // SIG // A1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEA // SIG // VrEqfq5rMRS3utQBPdCnp9lz4EByQ4kuEmy4b831Ywzw // SIG // 5jnURO+bkKIWIRTHRsBym1ZiytJR1dQKc/x3ImaKMnqA // SIG // L5B0Gh5i4cARpKMgAFcXGmlJxzSFEvS73i9ND8JnEgy4 // SIG // DdFfxcpNtEKRwxLpMCkfJH2gRF/NwMr0M5X/26AzaFih // SIG // IKXQLC/Esws1xS5w6M8wiRqtEc8EIHhAa/BOCtsENlly // SIG // P2ScWUv/ndxXcBuBKwRc81Ikm1dpt8bDD93KgkRQ7SdQ // SIG // t/yZ41zAoZ5vWyww9cGie0z6ecGHb9DpffmjdLdQZjsw // SIG // o/A5qirlMM4AivU47cOSlI2jukI3oB853V/7Wa2O/dnX // SIG // 0QF6+XRqypKbLCB6uq61juD5S9zkvuHIi/5fKZvqDSV1 // SIG // hl2CS+R+izZyslyVRMP9RWzuPhs/lOHxRcbNkvFML6wW // SIG // 2HHFUPTvhZY+8UwHiEybB6bQL0RKgnPv2Mc4SCpAPPEP // SIG // EISSlA7Ws2rSR+2TnYtCwisIKkDuB/NSmRg0i5LRbzUY // SIG // YfGQQHp59aVvuVARmM9hqYHMVVyk9QrlGHZR0fQ+ja1Y // SIG // RqnYRk4OzoP3f/KDJTxt2I7qhcYnYiLKAMNvjISNc16y // SIG // IuereiZCe+SevRfpZIfZsiSaTZMeNbEgdVytoyVoKu1Z // SIG // Qbj9Qbl42d6oMpva9cL9DLUwggdxMIIFWaADAgECAhMz // SIG // AAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUA // SIG // MIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu // SIG // Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV // SIG // TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylN // SIG // aWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3Jp // SIG // dHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAx // SIG // ODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX // SIG // YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD // SIG // VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV // SIG // BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw // SIG // MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA // SIG // 5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1 // SIG // V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeF // SIG // RiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc // SIG // wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus // SIG // 9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130 // SIG // /o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHI // SIG // NSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTes // SIG // y+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGp // SIG // F1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+ // SIG // /NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fz // SIG // pk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNO // SIG // wTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLi // SIG // Mxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5 // SIG // UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q // SIG // BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6H // SIG // XtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIG // SIG // CSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYE // SIG // FCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSf // SIG // pxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG // SIG // DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRw // SIG // Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3Mv // SIG // UmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUH // SIG // AwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD // SIG // VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j // SIG // BBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0f // SIG // BE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQu // SIG // Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 // SIG // XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBK // SIG // BggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQu // SIG // Y29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w // SIG // Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1Vffwq // SIG // reEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1 // SIG // OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpT // SIG // Td2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinL // SIG // btg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l // SIG // 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJ // SIG // w7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2Fz // SIG // Lixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7 // SIG // hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY // SIG // 3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFX // SIG // SVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFU // SIG // a2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz // SIG // /gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/ // SIG // AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1 // SIG // ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328 // SIG // y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG // SIG // ahC0HVUzWLOhcGbyoYIDTTCCAjUCAQEwgfmhgdGkgc4w // SIG // gcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n // SIG // dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN // SIG // aWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p // SIG // Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNV // SIG // BAsTHm5TaGllbGQgVFNTIEVTTjpBOTM1LTAzRTAtRDk0 // SIG // NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg // SIG // U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAq2mH9cQ5NqzJ // SIG // 1P1SaNhhitZ8aPGggYMwgYCkfjB8MQswCQYDVQQGEwJV // SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH // SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv // SIG // cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T // SIG // dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAOth // SIG // k1QwIhgPMjAyNTAyMjAxMTQxMDhaGA8yMDI1MDIyMTEx // SIG // NDEwOFowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA62GT // SIG // VAIBADAHAgEAAgIzQzAHAgEAAgITmDAKAgUA62Lk1AIB // SIG // ADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMC // SIG // oAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3 // SIG // DQEBCwUAA4IBAQCB6bFkVxIaOWQjf+1aQE344RxXmwpK // SIG // tx2Nwflw/twJfsw9sJ3BHttgW4uqIjTgusWqX3lQZIpH // SIG // iSvn5E5MTEJYWrmE99HhG/nGmUDgZWvnu7GzM2fGB8LD // SIG // iDhHaeo4+1ObGJlo7so2/0VWik4GEBdUTzxBPWU7Pecy // SIG // rF1BoEc+/gTJBEgklBz6tGdXEcKIFoN409dQPc4CPshu // SIG // CIYFn8Mbhvwgr9ovC08gktgo+LzUaBdaEerSrzgtZ/L4 // SIG // mkrFIsOXHDa263WJ8cnVmp516w7GxRvjN6nK+JFM4dOB // SIG // knpBL00LrFFa1dx1oLITu2A5r/GqRDLUJWncgwFhYgnc // SIG // oJ47MYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMx // SIG // EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl // SIG // ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh // SIG // dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh // SIG // bXAgUENBIDIwMTACEzMAAAHpD3Ewfl3xEjYAAQAAAekw // SIG // DQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzEN // SIG // BgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgjz8g // SIG // RYUVcATHltEBKEN73XPhNP9JKe/Jr+Xj++wkJzIwgfoG // SIG // CyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCkkJJ4l2k3 // SIG // Jo9UykFhfsdlOK4laKxg/E8JoFWzfarEJTCBmDCBgKR+ // SIG // MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n // SIG // dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN // SIG // aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p // SIG // Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB // SIG // 6Q9xMH5d8RI2AAEAAAHpMCIEIBoOgIpqeUYbXexrPgWQ // SIG // YyWYxe69AuNspnMoU1Yzp90pMA0GCSqGSIb3DQEBCwUA // SIG // BIICADfSzlqm+Te0MqMU31e9zXmXa0/l7nBknKLlyZ16 // SIG // g4DI31xN6AdDmDV9Nf16kpUWoa4dyHnAjmziBZNIsDgQ // SIG // mLuvYiHSUNhUM7aIRv20in7v99ol4M/72NT/prZKzKJG // SIG // 8wDx0449t3IXU823PRiCZFn3MnL/9tKfVVisNSvgoXb4 // SIG // SObhFBPG5sA8w8IUzEJ+oirhKkfpPAUAp2FZ7/qTAohv // SIG // EPLSq4RMFhVGoJPsqz90jjZWKz8p47A4EiTrFGoKweyI // SIG // SR39uNOA6fuyh6jAtEJG+UzrpdAnsB5fWNp0J6zMej7m // SIG // eFIBCIs6dHyrz9qIu61O18YEdidTcoNO9E/k44YPpBnA // SIG // +oYQcxFIOZFe71b+kAt7iqX+SdXDRCGVV1LYY5MP+Tsb // SIG // KlqlFxnmIcjeMyUIfTaDXaIYAYEhitfUnFgLVkd4LlJT // SIG // 7ckVgCXIGHZknjc5pnjzW9Jo9Z1kod5d1PivIK4YIbuh // SIG // biw1vL8EVGXsmBqiccFsgh94n7jdp1HFiGrWQwGK2OzZ // SIG // xZVFVRgpvGImwx8JwzxlHhHpvyUdBnTzdwLmdUK3tCLr // SIG // dNnxzFg5r/piqe0kMOga1HjEjjPV017uZuHtosW9uM15 // SIG // hGtnm0cB29mX8idcgx+pGipBQNYNqvBGVIDSbM/0pFBK // SIG // qHkzoo1puC5LYW4/FC2xnDdQ/gNy // SIG // End signature block