@microsoft/windows-admin-center-sdk
Version:
Microsoft - Windows Admin Center Shell
944 lines (941 loc) • 42 kB
JavaScript
import { Observable, of, ReplaySubject, throwError } from 'rxjs';
import { catchError, expand, filter, map, mergeMap, switchMap, take, tap } from 'rxjs/operators';
import { EnvironmentModule } from '../manifest/environment-modules';
import { RpcObservableElevateClient } from '../rpc/elevate/rpc-observable-elevate-client';
import { RpcObservableElevateServer } from '../rpc/elevate/rpc-observable-elevate-server';
import { RpcServiceForwarder } from '../rpc/rpc-forwarder';
import { CoreEnvironment } from './core-environment';
import { GatewayUrlBuilder } from './gateway-url-builder';
import { GatewayUrls } from './gateway-urls';
import { Http, HttpMethod } from './http';
import { headerConstants } from './http-constants';
import { Net } from './net';
/**
* The Gateway Connection class for creating requests and calling the Gateway's REST API
*/
export class GatewayConnection extends RpcServiceForwarder {
http;
authorizationManager;
static rpcCommands = {
forbiddenReceived: 'forbiddenRecieved'
};
/**
* The IPv4 address for localhost
*/
localhostIpV4 = '127.0.0.1';
/**
* The IPv6 address for localhost
*/
localhostIpV6 = '::1';
/**
* The localhost name
*/
localhost = 'localhost';
/**
* Time interval to check for internalGatewayStatus refresh.
*/
lastUpdatedInternalGatewayStatusInterval = 1000 * 60 * 10; // 10 minutes
/**
* internally maintained gateway URL.
*/
internalGatewayUrl;
/**
* internally maintained static version.
*/
internalStaticVersion;
/**
* gatewayUrl param is specified.
*/
internalGatewayUrlSpecified;
/**
* Last time internal gateway status was updated in milliseconds
*/
lastUpdatedInternalGatewayStatus;
/**
* Internally maintained gateway status.
*/
internalGatewayStatus;
/**
* The replay subject for gateway url to settle.
*/
gatewayUrlAwaiter = new ReplaySubject(1);
/**
* The RPC observable elevate client.
*/
elevateClient = null;
/**
* The RPC observable elevate server.
*/
elevateServer = null;
/**
* The elevate callback observable.
*/
elevateCallbackInternal = () => of(false);
/**
* The replay subject for navigation ready state.
*/
navigationReady = new ReplaySubject(1);
/**
* Sets the elevate callback observable.
*/
set elevateCallback(value) {
this.elevateCallbackInternal = value;
if (!this.elevateServer) {
this.elevateServer = new RpcObservableElevateServer(this.rpc);
this.elevateServer.register((_context) => {
return this.elevateCallbackInternal()
.pipe(catchError((error, _caught) => throwError(() => ({ error: Net.getErrorMessage(error) }))), map(result => ({ elevated: result })));
});
}
}
/**
* Indicates that the gateway is disabled and therefore should not be called.
*/
get disabled() {
const environment = MsftSme.self().Environment;
return environment && environment.configuration.gateway.disabled;
}
/**
* Gets the gateway URL to connect to.
*/
get gatewayUrl() {
return this.internalGatewayUrl;
}
/**
* Gets the gateway URL specified state.
*/
get staticVersion() {
return this.internalStaticVersion;
}
/**
* Gets the gateway URL specified state.
*/
get gatewayUrlSpecified() {
return this.internalGatewayUrlSpecified;
}
/**
* Gets the gateway URL observable while setting up.
*/
get gatewayUrlObservable() {
return this.gatewayUrlAwaiter.asObservable();
}
/**
* Gets the navigation ready observable.
*/
get navigationReadyObservable() {
return this.navigationReady.asObservable();
}
/**
* Sets the navigation ready status.
*/
set navigationReadyStatus(ready) {
this.navigationReady.next(ready);
}
/**
* Gets the gateway information.
*/
get gatewayInfo() {
if (!this.internalGatewayUrl) {
throw new Error(MsftSme.getStrings().MsftSmeShell.Core.Error.GatewayUrlNotConfigured.message);
}
// RegEx: ('http' or 'https') '://' (('<gatewayName1>'):('<port>') or ('<gatewayName2>'))
// 0: url
// 1: https or http
// 2: <gatewayName1>:<port> or <gatewayName2>
// 3: <gatewayName1> or undefined
// 4: <port> or undefined
// 5: <gatewayName2>
const url = MsftSme.trimEnd(this.internalGatewayUrl.toLowerCase(), '/');
const match = url.match(/(http|https):\/\/((.+):(\d+)|(.+))/);
if (!match) {
throw new Error(MsftSme.getStrings().MsftSmeShell.Core.Error.GatewayUrlMalformed.message);
}
const secure = (match[1] === 'https');
const name = match[3] || match[2];
const port = parseInt(match[4], 10) || (secure ? 443 : 80);
return { name, secure, port };
}
/**
* Gets the gateway node name to make a CIM/PowerShell query to the gateway node.
*/
get gatewayName() {
// localhost will be used to locally query gateway node. if this causes any access problem, need to be replaced.
return 'localhost';
}
/**
* Gets gateway status from cache or sets cache if it doesn't exist.
*/
get gatewayStatus() {
if (!this.internalGatewayStatus
|| Date.now() - this.lastUpdatedInternalGatewayStatus > this.lastUpdatedInternalGatewayStatusInterval) {
return this.getStatus();
}
return of(this.internalGatewayStatus);
}
/**
* Initializes a new instance of the Gateway class.
*
* @param http the Http object.
* @param rpc the Rpc class.
* @patam authorizationManager the authorization manager class object.
*/
constructor(http, rpc, authorizationManager) {
super('gateway-connection', rpc);
this.http = http;
this.authorizationManager = authorizationManager;
// restore the AAD token if exist becase ealier access of token is required for gateway calls.
authorizationManager.signOnManager.applySignedHttpRequestToken(MsftSme.self().Environment.configuration?.signOn?.signedHttpRequestToken);
}
/**
* Configure the gateway URL to connect to.
*/
configureGatewayEnvironment(url, urlSpecified, staticVersion) {
// trim last "/"
if (url[url.length - 1] === '/') {
url = url.substring(0, url.length - 1);
}
this.internalGatewayUrl = url;
this.internalGatewayUrlSpecified = urlSpecified,
this.internalStaticVersion = staticVersion;
this.gatewayUrlAwaiter.next(url);
}
/**
* Update the url with static version option.
* @param url the original URL including options.
* @returns updated url with "version=" option if staticVersion present.
*/
addStaticVersion(url) {
if (MsftSme.isNullOrWhiteSpace(this.staticVersion) || this.staticVersion.indexOf('staticVersion=') < 0) {
return url;
}
const splitter = url.indexOf('?') > 0 ? '&' : '?';
return `${url}${splitter}staticVersion=${this.staticVersion}`;
}
/**
* Makes a POST call to the gateway
*
* @param relativeUrl the relative Url after "/api"
* @param body the body string JSON.stringfy'ed
* @param request the gateway request object.
*/
post(relativeUrl, body, request) {
const postRequest = this.createRequest(HttpMethod.Post, relativeUrl, body, request);
return this.call(postRequest);
}
/**
* Makes a GET call to the gateway
*
* @param relativeUrl the relative Url after "/api"
* @param request the gateway request object.
*/
get(relativeUrl, request) {
const getRequest = this.createRequest(HttpMethod.Get, relativeUrl, null, request);
return this.call(getRequest);
}
/**
* Makes a PUT call to the gateway
*
* @param relativeUrl the relative Url after "/api"
* @param body the body string JSON.stringfy'ed
* @param request the gateway request object.
*/
put(relativeUrl, body, request) {
const putRequest = this.createRequest(HttpMethod.Put, relativeUrl, body, request);
return this.call(putRequest);
}
/**
* Makes a PATCH call to the gateway
*
* @param relativeUrl the relative Url after "/api"
* @param body the body string JSON.stringfy'ed
* @param request the gateway request object.
*/
patch(relativeUrl, body, request) {
const patchRequest = this.createRequest(HttpMethod.Patch, relativeUrl, body, request);
return this.call(patchRequest);
}
/**
* Makes a DELETE call to the gateway
*
* @param relativeUrl the relative Url after "/api"
* @param body the body string JSON.stringfy'ed
* @param request the gateway request object.
*/
delete(relativeUrl, body, request) {
const deleteRequest = this.createRequest(HttpMethod.Delete, relativeUrl, body, request);
return this.call(deleteRequest);
}
/**
* Makes a DELETE call to the gateway without waiting for the response.
*
* @param relativeUrl the relative Url after "/api"
* @param request the gateway request object.
*/
deleteQuick(relativeUrl, headers) {
headers[headerConstants.MODULE_NAME] = EnvironmentModule.getModuleName();
headers[headerConstants.MODULE_VERSION] = EnvironmentModule.getModuleVersion();
headers[headerConstants.ACCEPT_LANGUAGE] = CoreEnvironment.localizationManager.getLocaleId().neutral;
if (this.staticVersion) {
headers[headerConstants.STATIC_VERSION] = this.staticVersion;
}
if (this.authorizationManager.signOnManager.isSignOnTokenEnabled) {
this.authorizationManager.signOnManager.SetAadAuthorizationHeader(headers);
}
const url = Net.gatewayApi(this.gatewayUrl, relativeUrl);
this.http.deleteQuick(url, headers);
}
/**
* Creates a GatewayRequest.
*
* @param method the http method to use
* @param relativeUrl the relative Url after "/api/"
* @param body the body string JSON.stringfy'ed
* @param request the gateway request object to extend.
*/
createRequest(method, relativeUrl, body, request) {
const defaultMaxRetry = 3;
request = MsftSme.deepAssign({}, request);
// if request is undefined, default to empty object
request = request || {};
request.headers = request.headers || {};
request.headers[headerConstants.MODULE_NAME] = EnvironmentModule.getModuleName();
request.headers[headerConstants.MODULE_VERSION] = EnvironmentModule.getModuleVersion();
request.headers[headerConstants.ACCEPT_LANGUAGE] = CoreEnvironment.localizationManager.getLocaleId().neutral;
if (this.staticVersion) {
request.headers[headerConstants.STATIC_VERSION] = this.staticVersion;
}
// use default retry options if none are provided
const retryHandlers = [];
if (this.authorizationManager.signOnManager.isSignOnTokenEnabled) {
this.authorizationManager.signOnManager.SetAadAuthorizationHeader(request.headers);
retryHandlers.push({
canHandle: (code, error) => this.authorizationManager.signOnManager.canHandleUnauthorizedLogin(code, error),
handle: (code, originalRequest, error) => this.authorizationManager.signOnManager.handleUnauthorizedLogin(code, originalRequest, error)
});
}
if (request.retryHandlers != null && request.retryHandlers.length > 0) {
retryHandlers.push(...request.retryHandlers);
}
return Object.assign(request, {
method: method,
url: relativeUrl,
// default to the passed in body, the request body, or an empty string
body: body || request.body || '',
// default to the request headers, or an empty object
headers: request.headers,
// for the next 2 props, default to true unless explicitly set to false
withCredentials: request.withCredentials === false ? false : true,
crossDomain: request.crossDomain === false ? false : true,
createXHR: () => new XMLHttpRequest(),
retryHandlers,
maxRetryCount: request.maxRetryCount === 0 ? 0 : request.maxRetryCount || defaultMaxRetry
});
}
/**
* Make a request.
*
* @param request the request to execute against the gateway.
* @return Observable<any> the query result observable.
*/
call(request) {
if (!this.gatewayUrl) {
return this.gatewayUrlAwaiter.pipe(mergeMap(() => this.call(request)));
}
// create gateway url from current url if not set yet.
if (!request.url || (!request.url.startsWith('http://') && !request.url.startsWith('https://'))) {
request.url = Net.gatewayApi(this.gatewayUrl, request.url);
}
// create retry options from request
const retryOptions = {
handlers: (request.retryHandlers || []),
maxRetry: request.maxRetryCount
};
// create observable for our request
const requestObservable = this.http.request(request, retryOptions)
.pipe(map((response) => response ? response.response : {}));
if (request.beforeCall) {
return request.beforeCall(request).pipe(mergeMap(() => requestObservable));
}
return requestObservable;
}
/**
* Gets default secure request options.
*
* @returns updated request object.
*/
get defaultHttpSecureOptions() {
const request = { ...{ headers: {} }, ...Http.defaultHttpOptions };
if (this.authorizationManager.signOnManager.isSignOnTokenEnabled) {
this.authorizationManager.signOnManager.SetAadAuthorizationHeader(request.headers);
}
return request;
}
/**
* Check if elevation is required from the error object.
*
* @param error the ajax error object.
*/
isElevationRequired(error) {
const code = error.xhr && error.xhr.response && error.xhr.response.error && error.xhr.response.error.code;
return code === 'ElevationRequired';
}
/**
* Elevate the gateway if it's desktop mode running.
*/
elevate() {
if (!this.elevateClient) {
this.elevateClient = new RpcObservableElevateClient(this.rpc);
}
return this.elevateClient.elevate().pipe(map(result => result.elevated));
}
/**
* Creates and returns a new URL builder for the current connection.
*/
url() {
return new GatewayUrlBuilder(this.gatewayUrl);
}
/**
* Clear the DNS cache.
*/
clearDnsCache() {
const dnsCacheClearUrl = 'gateway/dnsCacheClear';
return this.delete(dnsCacheClearUrl)
.pipe(map(_ => null));
}
/**
* Polling to check if the gateway is elevated.
*/
pollingGatewayElevated() {
// user might not respond for elevation prompt, maximum waiting time is 2.5 min.
// getElevatedStatus() takes 5000ms when the gateway is not responding.
// 2.5 min = count [30] * 5000 msec.
const uniQueId = MsftSme.getUniqueId();
const maxWaitingCount = 30;
let count = maxWaitingCount;
return this.getElevatedStatus()
.pipe(expand(status => {
// user accepted.
// adds 5 calls for the gateway dead period.
// < Restart Requested from UI >
// 1st query: it shows success on slow performed system, the gateway is even not shutdown yet.
// 2nd query: It could success still.
// 3rd query: Maybe timeout because the gateway is not started yet.
// 4rd query: Maybe timeout because the gateway is not started yet.
// 5rd query: Maybe timeout because the gateway is down.
// 25 = 30 - 5
const pollingStartCount = 25;
if (((count <= pollingStartCount
&& (!status.error || status.isGatewayProcessElevated))
|| status.completed)
&& (status.id === uniQueId)) {
return of({
completed: true,
id: uniQueId,
isGatewayProcessElevated: status.isGatewayProcessElevated
});
}
// no response for more than 2.5 minutes.
// gateway was stopped for long or user never responded to UAC prompt.
if (count-- <= 0 && status.id === uniQueId) {
return of({
completed: true,
id: uniQueId,
isGatewayProcessElevated: status.isGatewayProcessElevated
});
}
return this.getElevatedStatus();
}), filter(result => result.isGatewayProcessElevated || result.completed), take(1), map(result => result.isGatewayProcessElevated));
}
/**
* Perform gateway status query but cut off if it exceeds 2.5 seconds.
*/
getElevatedStatus() {
// if the gateway is not responding for 5000ms, cancel the call.
const callCancelTime = 5000;
return new Observable(observer => {
let subscription = null;
let timer = setTimeout(() => {
// handle pending timeout of the query when the gateway doesn't respond.
if (subscription) {
observer.next({ error: true, isGatewayProcessElevated: false, completed: undefined, id: undefined });
observer.complete();
subscription.unsubscribe();
subscription = null;
timer = null;
}
}, callCancelTime);
subscription = this.getStatus()
.subscribe({
next: data => {
observer.next({
error: false,
isGatewayProcessElevated: data.isGatewayProcessElevated,
completed: undefined,
id: undefined
});
observer.complete();
subscription.unsubscribe();
subscription = null;
if (timer) {
clearTimeout(timer);
}
},
error: () => {
observer.next({ error: true, isGatewayProcessElevated: false, completed: undefined, id: undefined });
observer.complete();
subscription.unsubscribe();
subscription = null;
if (timer) {
clearTimeout(timer);
}
}
});
});
}
/**
* Gets gateway machine if found in list of given nodes
* @param nodes Node names or IPs to check
* @returns Node name of gateway if found, null otherwise
*/
getGateway(nodes) {
return this.gatewayStatus.pipe(map(status => {
for (const node of nodes) {
if (MsftSme.isNullOrUndefined(node)) {
continue;
}
switch (node.toLocaleLowerCase()) {
case this.localhostIpV4:
case this.localhostIpV6:
case this.localhost:
case status.fullyQualifiedDNSName?.toLocaleLowerCase():
case status.name?.toLocaleLowerCase():
case status.machineName?.toLocaleLowerCase():
return node;
}
if (status.addressList?.find((address) => address === node)) {
return node;
}
}
return null;
}));
}
/**
* Check gateway condition.
*/
checkCondition() {
const result = {};
return this.getStatus()
.pipe(switchMap(gateway => {
if (gateway.gatewayMode === 'Service') {
result.isServiceMode = true;
result.isGatewayProcessElevated = false;
return this.getAccessCheck();
}
result.isServiceMode = false;
result.isGatewayProcessElevated = gateway.isGatewayProcessElevated;
return of(true);
}), map((isAdmin) => {
result.isGatewayAdmin = !!isAdmin;
return result;
}), take(1));
}
/**
* Get gateway status.
*/
getStatus() {
return this.get('gateway/status').pipe(tap(status => {
this.lastUpdatedInternalGatewayStatus = Date.now();
this.internalGatewayStatus = status;
}));
}
/**
* Get user access check.
*/
getAccessCheck() {
return this.get(GatewayUrls.accessCheck);
}
/**
* Called on a child service instance when onForwardInit returns from the parent
* Initialize telemetry from within here so we can leverage gateway status for telemetry metadata.
* @param data The response from the forwardInit call
*/
onForwardInitResponse(data) {
if (data.error) {
// if there is an error, we cannot continue, so throw its
throw data.error;
}
if (!MsftSme.self().Environment.configuration.gateway.disabled) {
this.internalGatewayUrl = data.result.gatewayName;
this.internalGatewayUrlSpecified = data.result.gatewayUrlSpecified;
this.internalStaticVersion = data.result.staticVersion;
}
}
/**
* Called when a new instance of the service in another window is initialized and needs to synchronize with its parent
* @param from The RpcRelationshipType that this request is from
* @returns an observable for the all the values needed to initialize the service
*/
onForwardInit() {
if (this.gatewayUrl) {
return of({
gatewayName: this.gatewayUrl,
gatewayUrlSpecified: this.gatewayUrlSpecified,
staticVersion: this.staticVersion
});
}
else {
// if gateway value hasn't been set yet, then wait for it.
if (MsftSme.self().Environment.configuration.gateway.disabled) {
return of(null);
}
return this.gatewayUrlAwaiter
.pipe(map(url => ({
gatewayName: url,
gatewayUrlSpecified: this.gatewayUrlSpecified,
staticVersion: this.staticVersion
})));
}
}
/**
* Called when the forwarded services counterpart wants to get data from the parent
* @param from The RpcRelationshipType that this request is from
* @param name The name of the method to forward to
* @param args The arguments of the method
* @returns an observable for the result of the method call
*/
onForwardExecute(from, name) {
// gatewayConnection does not allow any method calls at this time
return this.nameNotFound(name);
}
/**
* Called when the forwarded services counterpart sends a notify message
* @param from The RpcRelationshipType that this request is from
* @param name The name of the property to change
* @param value The new value of the property
* @returns an observable that completes when the property has been changed.
*/
onForwardNotify(from, name) {
if (from === 1 /* RpcRelationshipType.Child */ && name === GatewayConnection.rpcCommands.forbiddenReceived) {
// Deprecated noop version 1.1100.0 at 10/1/2020. Using ErrorMonitor.
return of(null);
}
// gatewayConnection does not allow any other notifications at this time
return this.nameNotFound(name);
}
}
//# sourceMappingURL=gateway-connection.js.map
// SIG // Begin signature block
// SIG // MIIoOgYJKoZIhvcNAQcCoIIoKzCCKCcCAQExDzANBglg
// SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor
// SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC
// SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg
// SIG // ntTxCyHu9C6aEvUCPC3BZxZlfinFsnL5WD2cjix7cjqg
// 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 // ghoNMIIaCQIBATCBlTB+MQswCQYDVQQGEwJVUzETMBEG
// SIG // A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
// SIG // ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
// SIG // MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n
// SIG // IFBDQSAyMDExAhMzAAAEA73VlV0POxitAAAAAAQDMA0G
// SIG // CWCGSAFlAwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgor
// SIG // BgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE
// SIG // AYI3AgEVMC8GCSqGSIb3DQEJBDEiBCCosZxrI+2hnvtT
// SIG // 5p9g4Wc0kifLNDd50uH0UZzc1OFMmDBCBgorBgEEAYI3
// SIG // AgEMMTQwMqAUgBIATQBpAGMAcgBvAHMAbwBmAHShGoAY
// SIG // aHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3
// SIG // DQEBAQUABIIBAHtFajPeHbRQWbzkmqhU8fSkBKty0yBP
// SIG // 8HA2Xksin03Eu/kha0xPlLk5Z6BQNDNpxld5bULhIjde
// SIG // ITS/bCuSHF9ZuDImEcjtNL2xh3LlHfsp3JblWiZuyY1i
// SIG // Xf2J8S8pm+gzoV4FnH0OUFnK/+q+290+vcC6tv1p7HWk
// SIG // IlWlufi2sYqrQZfW6250VRFyMaIMxEf/QtRKKHY118d8
// SIG // 7aCL0VUlQxYebG+AEGVWIMwjDcvq/sJt0iOKfSqQiQ8x
// SIG // 5xHiFW9sOKKsGmv2kbWB6S2rNPFTWBcsGl9XkQ3mDI0/
// SIG // x8rtJEXzbbwO+qW+hz5MWtQaIROiYrmzmbt9ScOrsgkY
// SIG // UNOhgheXMIIXkwYKKwYBBAGCNwMDATGCF4Mwghd/Bgkq
// SIG // hkiG9w0BBwKgghdwMIIXbAIBAzEPMA0GCWCGSAFlAwQC
// SIG // AQUAMIIBUgYLKoZIhvcNAQkQAQSgggFBBIIBPTCCATkC
// SIG // AQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQg
// SIG // fX/kXdSir/b2LhwaUNn0Z1+Pyl616RDBDTB57PJw9wQC
// SIG // BmeuE6DXWxgTMjAyNTAyMjAxNTI4MzguMDkxWjAEgAIB
// SIG // 9KCB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgT
// SIG // Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
// SIG // BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMG
// SIG // A1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9u
// SIG // czEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjg5MDAt
// SIG // MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt
// SIG // ZS1TdGFtcCBTZXJ2aWNloIIR7TCCByAwggUIoAMCAQIC
// SIG // EzMAAAHt4V/L1felXXMAAQAAAe0wDQYJKoZIhvcNAQEL
// SIG // BQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
// SIG // bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
// SIG // FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
// SIG // TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN
// SIG // MjMxMjA2MTg0NTQxWhcNMjUwMzA1MTg0NTQxWjCByzEL
// SIG // MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
// SIG // EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
// SIG // c29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z
// SIG // b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMe
// SIG // blNoaWVsZCBUU1MgRVNOOjg5MDAtMDVFMC1EOTQ3MSUw
// SIG // IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
// SIG // aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
// SIG // AgEAqDDCbJK66mqcnC8TwtR+8w+4PPDaWxGkOko3EyEW
// SIG // 8wlcmEDbM/E2i9aahUxADy9V+6Iy+PxGhFvlIzambP2s
// SIG // jMluGCKRT2T9seBQTFQnXbHhdovmjDIwx4tC3E0GcTNr
// SIG // N5hTKwmQFkny2F2AyIphQc/I9KC1hst1YC5gUyjOMS6r
// SIG // +w2VM/AdkqAJmxLaetp4EpdITqDe90hcBPmNuErxkDFo
// SIG // cpKRvr1w8HKVr8A3vk4J6y0ewE0RVzeSUtunZtssukmB
// SIG // TEWJzBN3TBwkP1ECEEDQvJy5iL3SpAKFhDF7SbBhKN0K
// SIG // zNktkgb+D6R0c0bpM07T/lAHHhsTPScq8FED+TghJlum
// SIG // HIRMkQ0sD+IVPX+wdAModeD8PbyaO43sDY3jDyJJp3si
// SIG // 0otK7r9qMf8URrXCfcgTQuQWkZLY8+7LT2qI0fjwwNn7
// SIG // gbQqPMSpZLed5lG+wGPgmRx6oS5u+qXTBegR79k78JVQ
// SIG // XkETdtl42lVUcAoI4CZzXsLez3o3K6VJ9Khy4C6vtQTd
// SIG // Q82LpFpE6+8E9M5dIl6/jbalJFkjp1kX3vDdtbQerr91
// SIG // ZFvJxXQobW22Wc9uKXi7SRGbjazfF3/T0zzM2VwyQSNR
// SIG // HIkf/dUHBqGRZlWxVW9q1CALNNfmZHsL24ZhlQ3n1/aG
// SIG // RuPyuIBlLE701vq9SWTMVE9jMKkCAwEAAaOCAUkwggFF
// SIG // MB0GA1UdDgQWBBTOG/Ds994QKgRHypQGX9DSoeAiGTAf
// SIG // BgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
// SIG // BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv
// SIG // c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU
// SIG // aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYI
// SIG // KwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8v
// SIG // d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p
// SIG // Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEw
// SIG // KDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM
// SIG // MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkq
// SIG // hkiG9w0BAQsFAAOCAgEAndYCtkXfnhi9Hh4vohOv6g2P
// SIG // LG27DuHpmp3KeijzfsauWQGrYgUeI5kUYZvvVYpNBaNA
// SIG // y1ovzdvGxSO3V2MNPad7woqW48uBKIn5tDbne/+FN9Iv
// SIG // fu0b1u1zkN68d+/lO76vZZOsmKRgjadI5SdfPPwrkT4K
// SIG // iZ36uRHXmnx9gKBuOoSlk3gew7l2rBrerKSTnpxnnUq3
// SIG // t+DFmankyENK2jiLWZxbhg8uPsaA9akc9kFvrtoAh2hv
// SIG // AEI4WFDOLk4vbepLbY+nO12pq9s61rnHg4c+7Ci7bS0y
// SIG // e8LOWKwNiPHE5WzAH9iltBlYym2Bnfi2RnbhCu/+37OB
// SIG // lJDKnOdRbDXRtZ/s4HO8x7tqBhnggYOLPGUdGRcse47I
// SIG // JvbEhQikOQrGwL5a/+tDXZOU3jEfCbNpDVInLtgqmdN/
// SIG // N907L04JO4g9Si4H0tehxL4zepuFSiSoIyeziSc2m7Uu
// SIG // CUIFJyayGX8qLJOA9fK2Z4vaVMDdN1oE5nddFk8ImTru
// SIG // IPFJ+oinDzGP4hak7uJ7ZMxVMQQaWxRupNQiDWZqIqxF
// SIG // pgrRt5cmdiHXZo7SHn05CLxlm+Ccc8+5dpvxNpBjosQy
// SIG // R7GoWVZsLKDb4cuZLv4p1PvnVx88PoZb6k0hU/PayBVY
// SIG // XJrFjwyTzUUkqTIaCd91dmWv5ZCnG5FDNEm0IEvarHcw
// 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 // 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDUDCC
// SIG // AjgCAQEwgfmhgdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMw
// SIG // EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
// SIG // b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
// SIG // b24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9w
// SIG // ZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
// SIG // Tjo4OTAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
// SIG // b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsO
// SIG // AwIaAxUA7h2sikwmmLGMSYfqFk8erlTxcPmggYMwgYCk
// SIG // fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
// SIG // Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
// SIG // TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
// SIG // aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkq
// SIG // hkiG9w0BAQsFAAIFAOthI1IwIhgPMjAyNTAyMjAwMzQz
// SIG // MTRaGA8yMDI1MDIyMTAzNDMxNFowdzA9BgorBgEEAYRZ
// SIG // CgQBMS8wLTAKAgUA62EjUgIBADAKAgEAAgIeUwIB/zAH
// SIG // AgEAAgISqjAKAgUA62J00gIBADA2BgorBgEEAYRZCgQC
// SIG // MSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQow
// SIG // CAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQDFtsBB
// SIG // KC2jelFRIH2CDQeM3DtnNegpCDOhl0+j00u9EhAd85/o
// SIG // 0qBY59TRkhpLyBG2ZVnuJgap82ZhqQg74CMz0GKMIkBt
// SIG // FiRZcovtkex73M6SS/Ru0j8n2IbEbWDLRwYgVFV8jA3N
// SIG // AaZqav/7T99QTV1iRBYdYq+P4faTR4eVP0V1MRLX02JL
// SIG // F7s5GanB/Ij98LAQze6HbsHgzE1CYP5W7w15fh2jwJU3
// SIG // 5wWXTpoYUyfLsbGzGKybzQDcKEZV4SrzY5Fc5LXQ2UAC
// SIG // P6UgigZ6HkWlloCavzaWMWqZc7kzeEo9VBPZXOolN/i8
// SIG // 88O2OoOLIIULIIYHj1vTz8YokCrpMYIEDTCCBAkCAQEw
// SIG // gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
// SIG // bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
// SIG // FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd
// SIG // TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMA
// SIG // AAHt4V/L1felXXMAAQAAAe0wDQYJYIZIAWUDBAIBBQCg
// SIG // ggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAv
// SIG // BgkqhkiG9w0BCQQxIgQg/QN4PPgo7o8KmDbOjX4Qs/dq
// SIG // ruK+UD5tWSqFiAXvO3wwgfoGCyqGSIb3DQEJEAIvMYHq
// SIG // MIHnMIHkMIG9BCCNLg1oNAhbHisStwNepdcKyMK7Eg61
// SIG // 2esUn9BeMWzKOTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVT
// SIG // MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
// SIG // ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
// SIG // YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0
// SIG // YW1wIFBDQSAyMDEwAhMzAAAB7eFfy9X3pV1zAAEAAAHt
// SIG // MCIEIKehpqPZNMWvWSU4xN85KGCCJ8ISrlck3EcHy8ij
// SIG // fNkaMA0GCSqGSIb3DQEBCwUABIICAGlPCybScqnoBlSJ
// SIG // 9JPpAqo21S2XOIpbr645B5Z7kl7obBRFewefP5F9SHrr
// SIG // gqg9TNsGcAvcIQxoeTcDJ96ye4lyqc01iAf+mPyPYnr4
// SIG // rtcWHV2PuAgsOSvAhUd4UB5wq2Q5Z3f4Mb5yW3FURLib
// SIG // v3cIIcb8CJdW62vKTZRyO3L5h+tIvtE+fClQUxFq3RYJ
// SIG // g6mwQLhU2x7hTS1OgGCiOJLdDULOGgeb4w3gqvW223zx
// SIG // 0IHBfKTa9VEmBtDessR5jcWYdxFcHtWVLuBbb1TPcEgn
// SIG // Ei+yqppGlo8/VcN6l0Ck2qfoUEAs3SQojvGv49sAdSn2
// SIG // nX2XhpSgt/uDT2XSg8PtuSPH0dJ3hTbLzUSlYA03HtC/
// SIG // pbZrh5N07zNFt3mA3gzAWboAQVROPm8YFCAB0bP10Dfb
// SIG // M9BG7S1R3FVfLgtu4A+KQueuU1W6hi6i+OIowexUnrj8
// SIG // tH7W7cVDy/NniBvZj5WLiXVCvbqZ6qTGKFqzA08iFU/p
// SIG // CCdCpvcWnPFFJby41ab15AHGfP5e53+rX6Hy+CXXgior
// SIG // IV8dc45J58xM26GUMjjuJNu9O3/zzO4tOlz0QT+8lba4
// SIG // i5qXzgF04oCdC1ji2HCYOXZilGmzmk0bUG4UK6RdarF9
// SIG // 4q3wjhc5YwImnV3aS90CpTZZeL1DeDzh0xeyox/dtP70
// SIG // UK/EFGKp
// SIG // End signature block