Compare commits

..

34 Commits

Author SHA1 Message Date
akurilenko 2845a7d528 Merge pull request 'DDB-2156: [BACK]. [Подписание доверенности] — [КЭП]' (#7) from DDB-2156 into master
build (signer) TeamCity build finished Details
Reviewed-on: #7
2025-12-10 17:15:23 +03:00
cherednik bf677487d3 DDB-2156: [BACK]. [Подписание доверенности] — [КЭП]
build (signer) TeamCity build finished Details
2025-12-03 14:49:15 +04:00
cherednik bd3d4b3303 master: shutdown install license
build (signer) TeamCity build finished Details
2025-09-22 19:59:19 +04:00
dcherednik 0e560b59b1 Merge pull request 'fix-php-version: set version to 8.3' (#6) from fix-php-version into master
build (signer) TeamCity build failed Details
Reviewed-on: #6
2025-05-15 15:39:39 +03:00
cherednik 3dfe7dba30 fix-php-version: set version to 8.3
build (signer) TeamCity build finished Details
2025-05-15 16:34:36 +04:00
dcherednik b7d24cd749 Merge pull request 'DDB-1753' (#5) from DDB-1753 into master
build (signer) TeamCity build finished Details
Reviewed-on: #5
2025-03-21 13:37:34 +03:00
cherednik a4a12752b8 DDB-1753: fix response
build (signer) TeamCity build finished Details
2025-03-21 13:43:09 +04:00
cherednik 6533368534 DDB-1753: fix response 2025-03-21 13:40:10 +04:00
StSet 82ae1cbdf8 DDB-1753 - доработка формата ответа для сервиса подписания - fix 2025-03-21 12:32:57 +03:00
StSet 6943c9c41f DDB-1753 - доработка формата ответа для сервиса подписания - fix 2025-03-21 12:27:37 +03:00
StSet 52d40c6249 DDB-1753 - доработка формата ответа для сервиса подписания 2025-03-21 12:15:22 +03:00
cherednik 484dd35faa DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] - ПОДПИСАНИЕ КРИПТО ПРО fix nginx
build (signer) TeamCity build finished Details
2025-03-10 16:56:17 +04:00
cherednik 73d6b04a10 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] - ПОДПИСАНИЕ КРИПТО ПРО fix nginx 2025-03-10 16:50:54 +04:00
dcherednik bbe7d1b7fe Merge branch 'master' into DD-3602 2025-03-10 14:23:05 +03:00
cherednik ab1562212f fix Signer 2025-02-06 17:08:38 +04:00
ddadmin 180b2da20d Merge pull request 'move-php' (#4) from move-php into main
Reviewed-on: #4
2025-01-16 18:23:32 +03:00
Sergey Paramoshkin 983c886cc8 fix files 2025-01-16 17:57:18 +03:00
ddadmin 9ad05be425 Update Dockerfile 2025-01-16 17:53:51 +03:00
ddadmin a83662d4a4 Update Dockerfile 2025-01-16 17:47:39 +03:00
Sergey Paramoshkin 375f72c20f move to fpm 2025-01-14 09:52:53 +03:00
cherednik 2dd08fcaac DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-10 13:31:54 +04:00
cherednik cea9bdedaf DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-10 12:29:02 +04:00
cherednik c51d199b51 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 20:00:11 +04:00
cherednik c4c29fa2a7 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 19:56:50 +04:00
cherednik da1232c928 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 19:29:31 +04:00
cherednik eda759e7aa DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 19:19:13 +04:00
cherednik 2c29f7210b DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 19:15:27 +04:00
cherednik 011503b6a0 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 19:08:14 +04:00
cherednik 09ab8eb13c DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 18:42:58 +04:00
cherednik a16b8e45fb DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 18:39:40 +04:00
cherednik 4947b8ea19 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 18:37:38 +04:00
cherednik 6e87f37ad3 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 17:22:36 +04:00
cherednik fd46bbb399 DD-3602: [BACK]. [Доверенность водителю с КЭП] - [реализовать отправку документа через АПИ Диадок] 2025-01-09 17:15:30 +04:00
akurilenko 1d5e3c94a6 Merge pull request 'TD-322: [BACK]. [реализовать сервис для подписания документов в контейнере signer]' (#2) from TD-322 into main
Reviewed-on: #2
Reviewed-by: Alexey Muravov <muravov@dot-dot.ru>
Reviewed-by: eshatokhin <shatokhin@it.dot-dot.ru>
Reviewed-by: agubkin <ag@it.dot-dot.ru>
2024-10-28 14:44:12 +03:00
29 changed files with 726 additions and 321 deletions

View File

@ -4,12 +4,12 @@ ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y wget libgtk-3-0 libpcsclite-dev pcscd curl software-properties-common git zip bash
RUN add-apt-repository ppa:ondrej/php
RUN apt update && apt install -y \
php-fpm \
php-curl \
php \
php-common \
php-cli \
php-xml \
php8.3-fpm \
php8.3-curl \
php8.3 \
php8.3-common \
php8.3-cli \
php8.3-xml \
gnupg \
g++ \
procps \
@ -24,6 +24,7 @@ RUN apt update && apt install -y \
libonig-dev \
libxslt1-dev \
acl \
nginx \
&& echo 'alias sf="php bin/console"' >> ~/.bashrc
RUN wget -q -O /etc/apt/trusted.gpg.d/lab50.gpg http://packages.lab50.net/lab50.gpg
@ -54,7 +55,7 @@ RUN apt update && apt install -y okular-csp-utils
RUN mkdir -p /root/.config
COPY license.key /license.key
RUN echo Y | 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
@ -63,17 +64,19 @@ WORKDIR /usr/src/signer/
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/
COPY ./docker/nginx/default.conf /etc/nginx/sites-enabled/default
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
RUN mkdir -p /run/php
# 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
# RUN service php8.3-fpm start
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["php-fpm8.3", "-F"]
# CMD ["php-fpm8.3", "-F", "nginx", "-g 'daemon off'"]
EXPOSE 9000

79
Dockerfile_local Normal file
View File

@ -0,0 +1,79 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y wget libgtk-3-0 libpcsclite-dev pcscd curl software-properties-common git zip bash
RUN add-apt-repository ppa:ondrej/php
RUN apt update && apt install -y \
php8.3-fpm \
php8.3-curl \
php8.3 \
php8.3-common \
php8.3-cli \
php8.3-xml \
gnupg \
g++ \
procps \
git \
unzip \
zlib1g-dev \
libzip-dev \
libfreetype6-dev \
libpng-dev \
libjpeg-dev \
libicu-dev \
libonig-dev \
libxslt1-dev \
acl \
&& 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
COPY 3party/cprocsp/linux-amd64_deb.tgz /tmp/src/
RUN cd /tmp/src && \
tar -xf linux-amd64_deb.tgz && \
linux-amd64_deb/install.sh && \
dpkg -i linux-amd64_deb/cprocsp-pki-cades-64*.deb && \
dpkg -i linux-amd64_deb/cprocsp-rdr-* && \
# делаем симлинки
cd /bin && \
ln -s /opt/cprocsp/bin/amd64/certmgr && \
ln -s /opt/cprocsp/bin/amd64/cpverify && \
ln -s /opt/cprocsp/bin/amd64/cryptcp && \
ln -s /opt/cprocsp/bin/amd64/csptest && \
ln -s /opt/cprocsp/bin/amd64/csptestf && \
ln -s /opt/cprocsp/bin/amd64/der2xer && \
ln -s /opt/cprocsp/bin/amd64/inittst && \
ln -s /opt/cprocsp/bin/amd64/wipefile && \
ln -s /opt/cprocsp/sbin/amd64/cpconfig && \
#прибираемся
rm -rf /tmp/src
COPY 3party/okular/install.sh /tmp/
RUN bash /tmp/install.sh && rm -fv /tmp/install.sh
RUN apt update && apt install -y okular-csp-utils
RUN mkdir -p /root/.config
COPY license.key /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
WORKDIR /usr/src/signer/
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" ]
CMD ["php-fpm8.3", "-F"]
EXPOSE 9000

579
backend/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,4 +10,5 @@ return [
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
Symfony\UX\Chartjs\ChartjsBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
];

View File

@ -0,0 +1,5 @@
twig_component:
anonymous_template_directory: 'components/'
defaults:
# Namespace & directory for components
App\Twig\Components\: 'components/'

View File

@ -1,5 +1,5 @@
controllers:
controllers_sign_document:
resource:
path: ../src/Controller/
namespace: App\Controller
path: ../src/SignDocument/Controller/
namespace: App\SignDocument\Controller
type: attribute

View File

@ -28,6 +28,6 @@ services:
GuzzleHttp\Client: '@guzzle.http_client'
App\Api\ApiParams:
App\SignDocument\Api\ApiParams:
arguments:
$endPointUrl: '%env(DOT_DOT_URL)%'

View File

View File

@ -0,0 +1,9 @@
<?php
namespace App\Infrastructure\Api\Response;
// Абстрактный респонс - от него наследуются нужные респонсы для стандартизации ответа в ApiHelperTrait -> createNewJsonResponse
abstract class AbstractResponse
{
}

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Infrastructure\External\Api;
use RuntimeException;
/**
* Враппер бинарных фалов, при создании класса содает новый временный файл,
* при необходимости сохраняет файл под новым именем
@ -23,4 +25,14 @@ class BinaryStringFileResult
$this->tempFileName = sprintf('%s/%s_%s', sys_get_temp_dir(), 'Document', time());
file_put_contents($this->tempFileName, $content);
}
public function remove(): bool
{
if (file_exists($this->tempFileName)) {
unlink($this->tempFileName);
return true;
}
throw new RuntimeException('Temp file not found');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Infrastructure\Traits;
use App\Infrastructure\Api\Response\AbstractResponse;
use ReflectionClass;
use Symfony\Component\HttpFoundation\JsonResponse;
trait ApiHelperTrait
{
public function createJsonResponse(array $body, string $message = 'OK', int $code = 200): JsonResponse
{
return new JsonResponse([
'message' => $message,
'body' => $body,
], $code);
}
public function createPaginateJsonResponse(array $items, int $limit, int $totalCount): JsonResponse
{
return $this->createJsonResponse(
[
'items' => $items,
'pages' => ceil($totalCount / $limit),
'totalCount' => $totalCount,
]
);
}
public function createJsonResponseFromObject(AbstractResponse $body, string $message = 'OK', int $code = 200): JsonResponse
{
return new JsonResponse([
'message' => $message,
'body' => $this->toArray($body),
], $code);
}
private function toArray(AbstractResponse $body): array
{
$reflect = new ReflectionClass($body);
$props = $reflect->getProperties();
$array = [];
foreach ($props as $prop) {
$prop->setAccessible(true);
$array[$prop->getName()] = $prop->getValue($body);
$prop->setAccessible(false);
}
return $array;
}
}

View File

View File

@ -2,18 +2,19 @@
declare(strict_types=1);
namespace App\Api;
namespace App\SignDocument\Api;
use App\Infrastructure\External\Api\AbstractApi;
use App\Infrastructure\External\Api\BinaryStringFileResult;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
class Api extends AbstractApi
{
public ApiParams $apiParams;
public function send(string $token, string $path, int $batch): array
public function send(string $token, string $path, int $order): array
{
$params = [
RequestOptions::HEADERS => [
@ -23,7 +24,7 @@ class Api extends AbstractApi
RequestOptions::MULTIPART => [
[
'name' => 'file',
'contents' => fopen($path, 'r'),
'contents' => file_get_contents($path),
'filename' => $path,
'headers' => [
'Content-Type' => '<Content-type header>',
@ -36,7 +37,7 @@ class Api extends AbstractApi
],
];
$response = $this->client->post(sprintf('%s%s%s', $this->apiParams->endPointUrl, '/api/v1/document/upload/batch/', $batch), $params);
$response = $this->client->post(sprintf('%s%s%s', $this->apiParams->endPointUrl, '/api/v1/documents/upload/carrier/order/', $order), $params);
return $this->responseHandler->setResponse($response)->getContentJsonToArray();
}

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\Api;
namespace App\SignDocument\Api;
class ApiParams
{

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App\SignDocument\Api\Request;
use App\Infrastructure\Http\RequestDtoInterface;
class DigitalSignatureRequest implements RequestDtoInterface
{
public string $url;
public string $apiToken;
}

View File

@ -2,13 +2,13 @@
declare(strict_types=1);
namespace App\Api\Request;
namespace App\SignDocument\Api\Request;
use App\Infrastructure\Http\RequestDtoInterface;
class SignRequest implements RequestDtoInterface
{
public string $url;
public int $batch;
public int $order;
public string $apiToken;
}

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\SignDocument\Api\Response;
use App\Infrastructure\Api\Response\AbstractResponse;
class DigitalSignatureResponse extends AbstractResponse
{
public function __construct(
public string $hash,
public string $content,
)
{
}
}

View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\SignDocument\Controller;
use App\Infrastructure\Traits\ApiHelperTrait;
use App\SignDocument\Api\Request\DigitalSignatureRequest;
use App\SignDocument\Services\DigitalSignatureService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DigitalSignatureController extends AbstractController
{
use ApiHelperTrait;
public function __construct(
private readonly DigitalSignatureService $digitalSignatureService
){
}
#[Route(path: '/digital/sign', name: 'app.digital.sign', methods: ['POST'])]
public function __invoke(Request $request, DigitalSignatureRequest $digitalSignatureRequest): Response
{
$token = $request->server->get('HTTP_AUTHORIZATION');
return $this->createJsonResponseFromObject(
$this->digitalSignatureService->getSignature($digitalSignatureRequest, $token)
);
}
}

View File

@ -2,15 +2,15 @@
declare(strict_types=1);
namespace App\Controller;
namespace App\SignDocument\Controller;
use App\Api\Request\SignRequest;
use App\SignService;
use App\SignDocument\Api\Request\SignRequest;
use App\SignDocument\Services\SignService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class SignController extends AbstractController
{

View File

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App;
namespace App\SignDocument\Services;
class DevSignService
{

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\SignDocument\Services;
use App\Infrastructure\External\Api\BinaryStringFileResult;
use App\SignDocument\Api\Api;
use App\SignDocument\Api\ApiParams;
use App\SignDocument\Api\Request\DigitalSignatureRequest;
use App\SignDocument\Api\Response\DigitalSignatureResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Exception;
use RuntimeException;
class DigitalSignatureService
{
private BinaryStringFileResult $document;
public function __construct(
private readonly Api $api,
private readonly ApiParams $apiParams,
){
}
public function __destruct()
{
$this->document->remove();
}
public function getSignature(DigitalSignatureRequest $request, string $token): DigitalSignatureResponse
{
if ($_ENV['API_TOKEN'] !== $request->apiToken) {
throw new AccessDeniedHttpException('Доступ запрещен');
}
$this->api->apiParams = $this->apiParams;
try {
$this->document = $this->api->download($request->url, $token);
exec(sprintf('cp %s %s.pdf', $this->document->tempFileName, $this->document->tempFileName));
exec(sprintf('cryptcp -sign -detached -der %s.pdf', $this->document->tempFileName));
$response = base64_encode(file_get_contents($this->document->tempFileName . '.pdf.sgn'));
return new DigitalSignatureResponse(
hash: $response,
content: base64_encode(file_get_contents(sprintf('%s.pdf', $this->document->tempFileName)))
);
} catch (Exception $e) {
throw new RuntimeException($e->getMessage());
}
}
}

View File

@ -3,7 +3,7 @@
declare(strict_types=1);
namespace App;
namespace App\SignDocument\Services;
class ProdSignService

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\SignDocument\Services;
use App\Infrastructure\External\Api\BinaryStringFileResult;
class RemoveExistingDocumentService
{
public function removeExistingDocument(BinaryStringFileResult $document): void
{
if (file_exists($document->tempFileName)) {
unlink($document->tempFileName);
}
}
}

View File

@ -2,27 +2,24 @@
declare(strict_types=1);
namespace App;
namespace App\SignDocument\Services;
use App\Api\Api;
use App\Api\ApiParams;
use App\Api\Request\SignRequest;
use App\Infrastructure\External\Api\BinaryStringFileResult;
use App\SignDocument\Api\Api;
use App\SignDocument\Api\ApiParams;
use App\SignDocument\Api\Request\SignRequest;
use Exception;
use RuntimeException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class SignService
{
private DevSignService $devSignService;
private ProdSignService $prodSignService;
public function __construct(
private Api $api,
private ApiParams $apiParams
private readonly Api $api,
private readonly ApiParams $apiParams,
private readonly DevSignService $devSignService,
private readonly ProdSignService $prodSignService,
private readonly RemoveExistingDocumentService $removeExistingDocumentService,
){
$this->devSignService = new DevSignService();
$this->prodSignService = new ProdSignService();
}
public function signDocument(SignRequest $request,string $token): array
{
@ -37,9 +34,9 @@ class SignService
$this->sign($document->tempFileName);
$response = $this->api->send($token, $document->tempFileName . '_sign.pdf', $request->batch);
$response = $this->api->send($token, $document->tempFileName . '_sign.pdf', $request->order);
$this->removeExistingDocument($document);
$this->removeExistingDocumentService->removeExistingDocument($document);
return $response;
} catch (Exception $e) {
@ -54,11 +51,4 @@ class SignService
'prod' => $this->prodSignService->sign($documentUrl),
};
}
private function removeExistingDocument(BinaryStringFileResult $document): void
{
if (file_exists($document->tempFileName)) {
unlink($document->tempFileName);
}
}
}

View File

@ -176,6 +176,18 @@
"symfony/ux-chartjs": {
"version": "v2.19.3"
},
"symfony/ux-twig-component": {
"version": "2.22",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.13",
"ref": "67814b5f9794798b885cec9d3f48631424449a01"
},
"files": [
"config/packages/twig_component.yaml"
]
},
"symfony/validator": {
"version": "6.2",
"recipe": {

View File

@ -1,6 +1,23 @@
server {
server_name _;
listen *:80;
index index.php index.html index.htm;
root /usr/src/signer/public;
root /application/public;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
fastcgi_pass signer:9000;
try_files $uri =404;
fastcgi_index index.php;
}
location ~* .php/ {
rewrite (.*.php)/ $1 last;
}
}

View File

@ -1,5 +1,6 @@
user www-data;
worker_processes 4;
daemon off;
pid /run/nginx.pid;
events {
@ -34,4 +35,3 @@ http {
fastcgi_buffer_size 32k;
}
daemon off;

View File

@ -1,22 +1,22 @@
-----BEGIN PGP MESSAGE-----
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
owGbwMvMwMX4m2NrnreKjyDj6QOLkhgyVczfViuVpRYVZ+bnKVkpKBkq6Sgo5WQm
p+YVp4L4iWmJyalmaea6pkapFromqYapupYWKUa6BklmiYYGhonGKcamIC0FRfkp
pcklIC352aU5iUW6KanF2SX5BUiS8Uj2GIEtykvMBdsSU2pgYpiKTCoAKYPEJBDH
yAhEGoMlTMzB7EQwaaCLXxpkRFISyJ7U3MTMHJBFKdkOmSV6KfklukCsV1QKkiwu
SSwqKQY7ysDIVNfQUNfIJMTAwAqMosDaKwoyi1JhSizASoxDjIytTC2BCKwkrzQ3
KbUoPj8tHhp4INWGtZ1MxiwMjFwMsmKKLAvEjAJfslRVr9u1bw8sFliZQDHAwMUp
ABM50Mz/h+e3d491m8Ttn3L/Zsx8eW3all/WKmXuXcIz8n3mSW34rSd01tBNYkFl
4zz7xEMlu0wWPFWc6ytbrPebd7Ucg/+LSUG20/ZfSNf/way0R/RetPeRepZo942K
dw4JcPE8+BQ0N+NX+afe5XeXX17z0/7gwsh+TwHdxeuupM/OP9MtsaiidorljRMG
RpZPHihbzHq8I3lXsrGdrE2y6rHlQY8FKhun14Rd2KX0Iv3YuQa5Gzf5bH/krllz
8MFX5rOytgkKFq1KRzIEHrMY2nFnd+7nbTeW99pXf+3C46UX38w4Oi9+j/POD0un
svwoncFgEn5Q8EPGpwnLPm4Md1O7PHvPvkn/zeefNOqVZHhl91pG8eMkkfV/wxJe
rtOUtOWbO2dHdUAzc3WZyq6wj1ZS3Ls7V6o5/Hm7//zyzz6ngy70KC2u4Pg/z/jM
t9eMqyf9/hDX5XOjtnk691RXzeZYRrXXk/ZO9TFWmO6w0ZFV9+8h/vMiB5O7eZ1N
Tj5bnJKlE5LfeFpsQ/WLj+V3xAJeX+XwSHab/vOh5LGtr+y+3t8cz6991viVxIFF
99OM3pbMLXpwIY3Pa4NiV63r+rzuinubWKfsmWN3uef5mqLvN8982bvy8eW7PXyV
Z5nlXzK2NK24ulalb7ry9Ys5d/MX3A8tvH7ckGdSt19Hu8LpyPsPqnyn9u7wX/Y5
MI/v0jVZQ5fiXx/msZ3yzOhbvjoHAA==
=l3X2
-----END PGP MESSAGE-----