diff --git a/.gitignore b/.gitignore
index bf7460d..469d302 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,26 @@
.DS_Store
.vscode
-.idea
+/.idea/
+/.phpunit.cache
+/node_modules
+/public/build
+/public/hot
+/public/storage
+/storage/*.key
+/vendor
+.env
+.env.backup
+.env.production
+.phpactor.json
+.phpunit.result.cache
+Homestead.json
+Homestead.yaml
+auth.json
+npm-debug.log
+yarn-error.log
+/.fleet
+/.idea
+/.vscode
+/.php-cs-fixer.php
+/.php-cs-fixer.cache
+.php_cs.cache
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b8..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/codeception.xml b/.idea/codeception.xml
deleted file mode 100644
index 4a9baa3..0000000
--- a/.idea/codeception.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/git_toolbox_blame.xml b/.idea/git_toolbox_blame.xml
deleted file mode 100644
index 7dc1249..0000000
--- a/.idea/git_toolbox_blame.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 696bfed..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
deleted file mode 100644
index 6eba0e8..0000000
--- a/.idea/php.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/phpspec.xml b/.idea/phpspec.xml
deleted file mode 100644
index 4311520..0000000
--- a/.idea/phpspec.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/phpunit.xml b/.idea/phpunit.xml
deleted file mode 100644
index aacfcdb..0000000
--- a/.idea/phpunit.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/signer.iml b/.idea/signer.iml
deleted file mode 100644
index dd914ac..0000000
--- a/.idea/signer.iml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index c14e619..41d2366 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,7 +24,7 @@ RUN apt update && apt install -y \
libonig-dev \
libxslt1-dev \
acl \
- && echo 'alias sf="php bin/console"' >> ~/.bashrc \
+ && echo 'alias sf="php bin/console"' >> ~/.bashrc
RUN wget -q -O /etc/apt/trusted.gpg.d/lab50.gpg http://packages.lab50.net/lab50.gpg
RUN echo 'deb http://packages.lab50.net/okular jammy main non-free' > /etc/apt/sources.list.d/okulargost.list
@@ -54,7 +54,7 @@ RUN apt update && apt install -y okular-csp-utils
RUN mkdir -p /root/.config
COPY license.key /license.key
-RUN pdfcpro install-license /license.key
+RUN echo Y | pdfcpro install-license /license.key
COPY Inter-Bold.ttf /usr/local/share/fonts/Inter-Bold.ttf
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
@@ -64,12 +64,16 @@ COPY ./backend /usr/src/signer
COPY ./docker/php/www.conf /etc/php/8.3/fpm/pool.d/www.conf
+RUN curl curl https://frankenphp.dev/install.sh | sh
+RUN mv /usr/src/signer/frankenphp /usr/local/bin/
+
ENV COMPOSER_ALLOW_SUPERUSER=1
RUN composer install
RUN service php8.3-fpm start
-#COPY entrypoint.sh /entrypoint.sh
-#
-#ENTRYPOINT [ "/entrypoint.sh" ]
+COPY entrypoint.sh /entrypoint.sh
+
+ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["php-fpm8.3", "-F"]
+
EXPOSE 9000
\ No newline at end of file
diff --git a/backend/.env b/backend/.env
index 62f06bc..5784151 100644
--- a/backend/.env
+++ b/backend/.env
@@ -1,23 +1,5 @@
-# In all environments, the following files are loaded if they exist,
-# the latter taking precedence over the former:
-#
-# * .env contains default values for the environment variables needed by the app
-# * .env.local uncommitted file with local overrides
-# * .env.$APP_ENV committed environment-specific defaults
-# * .env.$APP_ENV.local uncommitted environment-specific overrides
-#
-# Real environment variables win over .env files.
-#
-# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
-# https://symfony.com/doc/current/configuration/secrets.html
-#
-# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
-# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
-
-###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=850da55654c68f779822ea80d2b66a94
-###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
@@ -28,3 +10,6 @@ APP_SECRET=850da55654c68f779822ea80d2b66a94
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
###< doctrine/doctrine-bundle ###
+DOT_DOT_URL='http://dot-dot.local'
+
+API_TOKEN='secret'
\ No newline at end of file
diff --git a/backend/composer.json b/backend/composer.json
index 5068288..b04d8e4 100755
--- a/backend/composer.json
+++ b/backend/composer.json
@@ -17,6 +17,7 @@
"symfony/flex": "^2",
"symfony/framework-bundle": "6.2.*",
"symfony/runtime": "6.2.*",
+ "symfony/serializer": "6.2.*",
"symfony/ux-chartjs": "*",
"symfony/yaml": "6.2.*"
},
diff --git a/backend/composer.lock b/backend/composer.lock
index 4aa2c94..83bd001 100644
--- a/backend/composer.lock
+++ b/backend/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "bf87fa37fe523eb581063fa93c0fda64",
+ "content-hash": "0d9c45fd694083582028c80344c66518",
"packages": [
{
"name": "composer/package-versions-deprecated",
@@ -5393,6 +5393,107 @@
],
"time": "2023-07-13T14:28:09+00:00"
},
+ {
+ "name": "symfony/serializer",
+ "version": "v6.2.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/serializer.git",
+ "reference": "19083104e606ecf8a48baa8ed310c7a073887037"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/19083104e606ecf8a48baa8ed310c7a073887037",
+ "reference": "19083104e606ecf8a48baa8ed310c7a073887037",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "doctrine/annotations": "<1.12",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/dependency-injection": "<5.4",
+ "symfony/property-access": "<5.4",
+ "symfony/property-info": "<5.4.24|>=6,<6.2.11",
+ "symfony/uid": "<5.4",
+ "symfony/yaml": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.12|^2",
+ "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/filesystem": "^5.4|^6.0",
+ "symfony/form": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/http-kernel": "^5.4|^6.0",
+ "symfony/mime": "^5.4|^6.0",
+ "symfony/property-access": "^5.4|^6.0",
+ "symfony/property-info": "^5.4.24|^6.2.11",
+ "symfony/uid": "^5.4|^6.0",
+ "symfony/validator": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0",
+ "symfony/var-exporter": "^5.4|^6.0",
+ "symfony/yaml": "^5.4|^6.0"
+ },
+ "suggest": {
+ "psr/cache-implementation": "For using the metadata cache.",
+ "symfony/config": "For using the XML mapping loader.",
+ "symfony/mime": "For using a MIME type guesser within the DataUriNormalizer.",
+ "symfony/property-access": "For using the ObjectNormalizer.",
+ "symfony/property-info": "To deserialize relations.",
+ "symfony/var-exporter": "For using the metadata compiler.",
+ "symfony/yaml": "For using the default YAML mapping loader."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Serializer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/serializer/tree/v6.2.13"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-07-27T16:18:16+00:00"
+ },
{
"name": "symfony/service-contracts",
"version": "v3.5.0",
diff --git a/backend/config/services.yaml b/backend/config/services.yaml
index 2d6a76f..aede265 100644
--- a/backend/config/services.yaml
+++ b/backend/config/services.yaml
@@ -14,11 +14,20 @@ services:
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
- resource: '../src/'
+ resource: '../src/*'
exclude:
- - '../src/DependencyInjection/'
- - '../src/Entity/'
- '../src/Kernel.php'
+ - '../src/*/Api/{Request,Response}'
+ - '../src/*/{Exception,Entity,Dto,Enum,Helper,Model}'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
+
+ guzzle.http_client:
+ class: GuzzleHttp\Client
+
+ GuzzleHttp\Client: '@guzzle.http_client'
+
+ App\Api\ApiParams:
+ arguments:
+ $endPointUrl: '%env(DOT_DOT_URL)%'
\ No newline at end of file
diff --git a/backend/src/Api/Api.php b/backend/src/Api/Api.php
new file mode 100644
index 0000000..0ad4285
--- /dev/null
+++ b/backend/src/Api/Api.php
@@ -0,0 +1,56 @@
+ [
+ 'Authorization' => $token,
+ 'Accept' => 'application/json',
+ ],
+ RequestOptions::MULTIPART => [
+ [
+ 'name' => 'file',
+ 'contents' => fopen($path, 'r'),
+ 'filename' => $path,
+ 'headers' => [
+ 'Content-Type' => '',
+ ],
+ ],
+ [
+ 'name' => 'type',
+ 'contents' => 'attorney-signed'
+ ],
+ ],
+ ];
+
+ $response = $this->client->post(sprintf('%s%s%s', $this->apiParams->endPointUrl, '/api/v1/document/upload/batch/', $batch), $params);
+
+ return $this->responseHandler->setResponse($response)->getContentJsonToArray();
+ }
+
+ public function download(string $url, string $token): BinaryStringFileResult
+ {
+ $params = [
+ RequestOptions::HEADERS => [
+ 'Authorization' => $token,
+ ],
+ ];
+
+ $response = $this->client->get($url, $params);
+
+ return $this->responseHandler->setResponse($response)->getContentAsBinaryStingResult();
+ }
+}
\ No newline at end of file
diff --git a/backend/src/Api/ApiParams.php b/backend/src/Api/ApiParams.php
new file mode 100644
index 0000000..f920f32
--- /dev/null
+++ b/backend/src/Api/ApiParams.php
@@ -0,0 +1,13 @@
+server->get('HTTP_AUTHORIZATION');
+
+ return new JsonResponse($this->signService->signDocument($signRequest, $token));
}
}
\ No newline at end of file
diff --git a/backend/src/DevSignService.php b/backend/src/DevSignService.php
new file mode 100644
index 0000000..d9a8dc8
--- /dev/null
+++ b/backend/src/DevSignService.php
@@ -0,0 +1,13 @@
+client = $client;
+ $this->responseHandler = $responseHandler;
+ }
+}
diff --git a/backend/src/Infrastructure/External/Api/ApiSleep.php b/backend/src/Infrastructure/External/Api/ApiSleep.php
new file mode 100644
index 0000000..cccea40
--- /dev/null
+++ b/backend/src/Infrastructure/External/Api/ApiSleep.php
@@ -0,0 +1,13 @@
+ 0) {
+ sleep($time);
+ }
+ }
+}
diff --git a/backend/src/Infrastructure/External/Api/BinaryStringFileResult.php b/backend/src/Infrastructure/External/Api/BinaryStringFileResult.php
new file mode 100644
index 0000000..6331c2c
--- /dev/null
+++ b/backend/src/Infrastructure/External/Api/BinaryStringFileResult.php
@@ -0,0 +1,26 @@
+saveToTempFile($content);
+ }
+
+ private function saveToTempFile(string $content): void
+ {
+ $this->tempFileName = sprintf('%s/%s_%s', sys_get_temp_dir(), 'Document', time());
+ file_put_contents($this->tempFileName, $content);
+ }
+}
diff --git a/backend/src/Infrastructure/External/Api/ResponseHandler.php b/backend/src/Infrastructure/External/Api/ResponseHandler.php
new file mode 100644
index 0000000..3e60520
--- /dev/null
+++ b/backend/src/Infrastructure/External/Api/ResponseHandler.php
@@ -0,0 +1,110 @@
+response->getBody()->getSize());
+ }
+
+ /**
+ * Возвращает ответ в текстовом виде, применит фильтры при их наличие.
+ *
+ * @return string
+ */
+ public function getContent(): string
+ {
+ $content = (string)$this->response->getBody();
+
+ return $this->filterContent($content);
+ }
+
+ /**
+ * Добавляет фильтр контента иногда необходимо поправить ответ, что бы в итоге возвращался нужный тип данных.
+ * Бывает иногда сервер, возвращает массив данных и при преобразовании json_decode возвращается
+ * массив вместо stdClass.
+ */
+ public function addContentFilter(callable $filter): self
+ {
+ $this->contentFilters[] = $filter;
+
+ return $this;
+ }
+
+ /**
+ * Преобразует json в объект
+ *
+ * @return stdClass
+ */
+ public function getContentStdFromJson(): stdClass
+ {
+ return json_decode($this->getContent());
+ }
+
+ public function getContentAsBinaryStingResult(): BinaryStringFileResult
+ {
+ return new BinaryStringFileResult($this->getContent());
+ }
+
+ /**
+ * @return stdClass[]
+ */
+ public function getContentArrayStdFromJson(): array
+ {
+ return json_decode($this->getContent());
+ }
+
+ /**
+ * Преобразует json в массив.
+ */
+ public function getContentJsonToArray(): array
+ {
+ return $this->contentIsEmpty() ? [] : json_decode($this->getContent(), true);
+ }
+
+ /**
+ * Устанавливает ответ в обработчик.
+ */
+ public function setResponse(ResponseInterface $response): self
+ {
+ $this->response = $response;
+
+ return $this;
+ }
+
+ /**
+ * Фильтруем ответ
+ *
+ * @param string $content
+ *
+ * @return string
+ */
+ private function filterContent(string $content): string
+ {
+ if (!$this->contentFilters) {
+ return $content;
+ }
+
+ foreach ($this->contentFilters as $filter) {
+ $content = $filter($content);
+ }
+
+ $this->contentFilters = [];
+
+ return $content;
+ }
+}
diff --git a/backend/src/Infrastructure/Http/RequestDtoInterface.php b/backend/src/Infrastructure/Http/RequestDtoInterface.php
new file mode 100644
index 0000000..f809ecb
--- /dev/null
+++ b/backend/src/Infrastructure/Http/RequestDtoInterface.php
@@ -0,0 +1,7 @@
+getType();
+
+ if (empty($type) || !class_exists($type)) {
+ return false;
+ }
+
+ try {
+ $reflection = new ReflectionClass($type);
+ } catch (Exception $e) {
+ return false;
+ }
+
+ return $reflection->implementsInterface(RequestDtoInterface::class);
+ }
+
+ public function resolve(Request $request, ArgumentMetadata $argument): iterable
+ {
+ if (($class = $argument->getType()) === null || !class_exists($class)) {
+ throw new RuntimeException('dto exception');
+ }
+
+ $object = $this->serializer->deserialize($request->getContent(), $class, 'json');
+
+ if (is_object($object)) {
+ $this->validateObject($object);
+ }
+
+ yield $object;
+ }
+
+ private function validateObject(object $object): void
+ {
+ $errors = $this->validator->validate($object);
+
+ if (count($errors) > 0) {
+ /* todo получать из вне сообщение для ошибки */
+ throw new RuntimeException($errors, 'Ошибка валидации');
+ }
+ }
+}
diff --git a/backend/src/ProdSignService.php b/backend/src/ProdSignService.php
new file mode 100644
index 0000000..3207646
--- /dev/null
+++ b/backend/src/ProdSignService.php
@@ -0,0 +1,16 @@
+devSignService = new DevSignService();
+ $this->prodSignService = new ProdSignService();
+ }
+ public function signDocument(SignRequest $request,string $token): array
+ {
+ if ($_ENV['API_TOKEN'] !== $request->apiToken) {
+ throw new AccessDeniedHttpException('Доступ запрещен');
+ }
+
+ $this->api->apiParams = $this->apiParams;
+
+ try {
+ $document = $this->api->download($request->url, $token);
+
+ $this->sign($document->tempFileName);
+
+ $response = $this->api->send($token, $document->tempFileName . '_sign.pdf', $request->batch);
+
+ $this->removeExistingDocument($document);
+
+ return $response;
+ } catch (Exception $e) {
+ throw new RuntimeException($e->getMessage());
+ }
+ }
+
+ private function sign (string $documentUrl): void
+ {
+ match ($_ENV['APP_ENV']) {
+ 'dev' => $this->devSignService->sign($documentUrl),
+ 'prod' => $this->prodSignService->sign($documentUrl),
+ };
+ }
+
+ private function removeExistingDocument(BinaryStringFileResult $document): void
+ {
+ if (file_exists($document->tempFileName)) {
+ unlink($document->tempFileName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/entrypoint.sh b/entrypoint.sh
index d5bf05d..2d80d18 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -1,12 +1,14 @@
-#!/bin/bash -x
+#!/bin/bash -x
-if [[ $SITE == "prod"]];then
+APP_ENV="${APP_ENV:-dev}"
+
+if [[ ${APP_ENV#*=} == "prod" ]]; then
service pcscd start
cert=$(/opt/cprocsp/bin/amd64/csptest -keyset -enum_cont -verifyc -fq | grep Aktiv | awk -F'00 00' '{print $2}' | tr -d '\\')
/opt/cprocsp/bin/amd64/certmgr -inst -cont "${cert}" -store uMy
certmgr -list -store umy
SHA=$(certmgr -list -store umy | grep SHA | awk -F':' '{print $2}' | tr -d ' ')
-fi
+fi
# pdfcpro sign /mnt/t/123.pdf -out /mnt/t/123_sign.pdf -cert ${SHA} -text "\n\t\tПодписано ЭП\n\t\t{subject/cn}\n\t\tСертификат {sha1}\n\t\tДействителен от {since} до {until}\n\t\tДата {date}\n\t\t{subject/t}\n\t\t{subject/fullname}\n\t\t" -fontfile /usr/local/share/fonts/Inter-Bold.ttf -fontsize 8 -x 2 -y 2 -w 96 -h 9
diff --git a/license.key b/license.key
index e578514..1c81c64 100644
--- a/license.key
+++ b/license.key
@@ -1,22 +1,22 @@
-----BEGIN PGP MESSAGE-----
-owGbwMvMwMX4m2NrnreKjyDj6QN7khjSNt7+XK1UllpUnJmfp2SloGSopKOglJOZ
-nJpXnArmJxmkmBmaJukamRpY6pqYp5noJqYkJeomWViamCdZmqYYGIG1FBTlp5Qm
-l4C05GeX5iQW6RanFgHNRZKLR7IGJJyXmAu2IqbUwMTICEQap4JIE3MwOxFMGuji
-l1YAUYZJCGljYzBpAVZqCCaNECIwbSD7U3MTM3NADsjLL07Ny853yCzRS8kv0QVi
-vaJSkIriksSikmKQEiMDIxNdAwtdA7MQAwMrMIoCm1FRkFmUiqTEEqTEyNjK1BKI
-wErySnOTUovi89PiocEKUm1Y28lkzMLAyMUgK6bIskDMKPAlS1X1ul379sDih5UJ
-FDcMXJwCMJFVJvz/naTc7D4zPFV0Z8rd1HWtvfZuBsenCs1DXVpJ919fN5DamxEc
-tPnJWy3X4JuP9+nsCIqXz1d4ofrd/NS7KB5RbX9Bxg9bV075eMOBsbLysJPOnnn9
-FbFf9nJ1bdro06nlmm0sHHx/UcHXDgbz1qcKcttnmZnxP81h19XzrZ5r4K27p3fm
-4WbOdc+v56twRPJef5r+3znp3eLSWqsOtYZgy6vdRzcEJa18Yai76U/ODt2Nfw/x
-7HTX2vvxa9Wcqo1pO/2P+kx3f3GTbXeyQUfVnb9FCyfm28kb/K7/76+hnuH3dvvU
-feXvjJ7Oys9Sm3ybdd6HoBk3I3utbr/5YLM63zIoML3pYJX2izNLQ07tPfRg4u9t
-J/iT//BGPF3zm3nb/hXimerFpxbsbg5cGZkfJR8n0rXE55yr/JV9mmohf4QPS2uX
-nLLzerB4/1cz9s1Ca6Z/2OtnInHMhpXvxTNJpc5lcRe7e5q7+WUvGHSHPUjNeSQe
-qSc7q5sxd/qBc82v5M0f9Eeqma5qP7nzgqZ55QP7KWKt51h6y/2PXemtO88sbF1r
-GMqf/69hto1qaFsdi7vJnpN32+3+/nuycvKDOYlPuT4mnde9xv5n4luLDfeu/Ki2
-6GDxLivKP2fqZlVXYp295veBA8U337gnX3RMa9nm2dnS/otHUKQnsmraLZl3Ducl
-V57lnFC7XEyhi1lFsMcv7Ai/sbP5PAA=
-=FMIf
+owGbwMvMwMX4m2NrnreKjyDj6QN7khjSpXteViuVpRYVZ+bnKVkpKBkq6Sgo5WQm
+p+YVp4L4ZhbmaakmSYm6BhaWqbomSamJupYpyca6aYYGScZJxsbJ5qmpIC0FRfkp
+pcklIC352aU5iUW6xalFQHOR5OKRrAEJ5yXmgq2IKTUwMTICkcapINLEHMxOBJMG
+uvilFUCUYRJC2tgYTFqAlRqCSSOECEwbyP7U3MTMHJAD8vKLU/Oy8x0yS/RS8kt0
+gVivqBSkorgksaikGKTEyMDIRNfQQNfINMTAwAqMosBmVBRkFqUiKTHUNTIJMTK2
+MrUEIrCSvNLcpNSi+Py0eGiwglQb1nYyGbMwMHIxyIopsiwQMwp8yVJVvW7Xvj2w
++GFlAsUNAxenAExk6QL+PxwK68QK1M50+n6+5/nqf67rlhvB1Yef7bh99zJzstHz
+LSdsOD4ee3Ik5rf+FtaUfX+nGBpwGmrMb/IqOzC3VfN73/WFEgJb/s5ZocZguCI9
+aM214nT57238r+tcp/LOfrHoxLEt+j8V5rO17S65PaHVRr3Tv6th0icNiZJD3/er
+pxtUmHzQKw7h22jIypd3bV3W5teZ04U68jbX3np0wOuZ/fdFB/fUzm2bvHzn2b6p
+jJ7vt1WkLpz87dSD2ee47nx/MOUEa8cf3aN7Y+ten47zY/vieMFLSCD5Q9cMz8Kt
+5bEJfP1AByw+qXEiLNqt8Mi2ig1MT76fKje6vlelXuH6iqtrpn/9Grx7xcWTDkdD
+RF4bfJUzfHH1bsAqlxDL6pXvsxrDGZItzs+YtNPEYtXEzxGHA8vcW45M0lFaNyE9
+a9WTZcVZQVvCYk79nfktzmhiZ7voZqY6BqsD82/cPHjPTYA58dvKvNoK5jm2p3lc
+i05nXhGwcPwz80H/tvglz1S3rlj1NuCniG7j+8Vn3pdpTdpksTfgqv3ujf4WRzu4
+CrWidbcJSRe+2FDD4tRmdydlDmPKNoGEKd+nZv4okAtaey6r8cixDzn3o/6d7XD3
+YjUP/uijsCBSvfiM7a0l+QuKJkzg/t7vv261S9LjMk/dSLYdxoZ3d3Lp+FZuDP0u
+fnm1QWZSluMsx5972jPC+3d3zrjqXfnJ5Nt1XRYA
+=UzI0
-----END PGP MESSAGE-----