Skip to content

Commit

Permalink
Add extra commands
Browse files Browse the repository at this point in the history
  • Loading branch information
diev committed Jun 26, 2024
1 parent f49f17b commit dcd6d00
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 80 deletions.
113 changes: 56 additions & 57 deletions Api5704/Api.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,23 @@ limitations under the License.

namespace Api5704;

internal static class Api
public class Api
{
private static readonly TlsClient _client = new();
private static readonly Config _config = Program.Config;

#region public

// API
public const string certadd = nameof(certadd);
public const string certrevoke = nameof(certrevoke);
public const string dlanswer = nameof(dlanswer);
public const string dlput = nameof(dlput);
public const string dlputanswer = nameof(dlputanswer);
public const string dlrequest = nameof(dlrequest);

// Extra
public const string auto = nameof(auto); // dlrequest + dlanswer
public const string dir = nameof(dir); // dlrequest + dlanswer

/// <summary>
/// POST
/// certadd – добавление нового сертификата абонента.
Expand Down Expand Up @@ -78,7 +81,7 @@ public static async Task PostCertAsync(string cmd, string id, string cert, strin
};

using HttpResponseMessage response = await _client.PostAsync(cmd, content);
int result = await WriteResultFileAsync(resultFile, response);
(int result, _) = await ApiHelper.WriteResultFileAsync(resultFile, response, _config.CleanSign);

string o = result switch
{
Expand All @@ -97,21 +100,30 @@ public static async Task PostCertAsync(string cmd, string id, string cert, strin
/// dlput – передача от БКИ данных, необходимых для формирования и предоставления пользователям
/// кредитных историй сведений о среднемесячных платежах Субъекта.
/// dlrequest – запрос сведений о среднемесячных платежах Субъекта.
/// Extra
/// auto - запрос и получение сведений (dlrequest + dlanswer) за один запуск.
/// </summary>
/// <param name="cmd">Метод API ("dlput" или "dlrequest").</param>
/// <param name="cmd">Метод API ("dlput" или "dlrequest") или Extra "auto".</param>
/// <param name="file">Имя файла с запросом в кодировке utf-8.
/// Если в конфиге SignFile: true и файл без расширения .sig, то он будет подписан ЭП в формате PKCS#7
/// (рядом появится файл с расширением .sig, который и будет отправлен).</param>
/// <param name="resultFile">Имя файла для ответной квитанции.
/// Если в конфиге CleanSign: true, то будут очищенный файл и файл.sig в исходном формате PKCS#7.</param>
/// <param name="answerFile">Имя файла для получения информации (Extra, только при "dlauto").
/// Если в конфиге CleanSign: true, то будут очищенный файл и файл.sig в исходном формате PKCS#7.</param>
/// <returns>
/// 200 – результат запроса содержит информацию о результатах загрузки данных в базу данных КБКИ;
/// 202 – результат запроса содержит квитанцию с идентификатором ответа;
/// 400 – результат запроса содержит квитанцию с информацией об ошибке;
/// 495 - сервер не признает наш сертификат - доступ отказан.
/// Extra (dlanswer):
/// 200 – результат запроса содержит сведения о среднемесячных платежах Субъекта;
/// 202 – результат запроса содержит квитанцию с информацией об ошибке «Ответ не готов»;
/// 400 – результат запроса содержит квитанцию с информацией об ошибке, кроме ошибки «Ответ не готов».
/// В случае получения ошибки «Ответ не готов» клиент должен повторить запрос не ранее, чем через 1 секунду.
/// 404 - неправильный идентификатор.
/// </returns>
/// <exception cref="NotImplementedException"></exception>
public static async Task PostRequestAsync(string cmd, string file, string resultFile)
public static async Task PostRequestAsync(string cmd, string file, string resultFile, string? answerFile = null)
{
if (!File.Exists(file))
{
Expand All @@ -126,7 +138,7 @@ public static async Task PostRequestAsync(string cmd, string file, string result
}

ByteArrayContent content = new(File.ReadAllBytes(file));
using HttpResponseMessage response = await _client.PostAsync(cmd, content);
using HttpResponseMessage response = await _client.PostAsync(cmd.Equals(auto) ? dlrequest : cmd, content);

if ((int)response.StatusCode == 495)
{
Expand All @@ -135,7 +147,8 @@ public static async Task PostRequestAsync(string cmd, string file, string result
Environment.Exit(495);
}

int result = await WriteResultFileAsync(resultFile, response);
(int result, string xml) = await ApiHelper.WriteResultFileAsync(resultFile, response, _config.CleanSign);
response.Dispose();

string o = result switch
{
Expand All @@ -147,7 +160,32 @@ public static async Task PostRequestAsync(string cmd, string file, string result

Console.WriteLine($"Ответ {result} - {o}");

Environment.Exit(result);
if (!cmd.Equals(auto))
{
Environment.Exit(result);
}

if (result != 202)
{
Console.WriteLine("Автоматическое получение ответа невозможно.");

Environment.Exit(result);
}

// GetAnswerAsync

Console.WriteLine("Этап 2 - получение сведений...");

XmlDocument doc = new();
doc.LoadXml(xml);
string id = doc.DocumentElement!.FirstChild!.InnerText;

if (answerFile is null)
{
throw new ArgumentNullException(answerFile, "Answer filename required.");
}

await GetAnswerAsync(dlanswer, id, answerFile);
}

/// <summary>
Expand All @@ -160,7 +198,7 @@ public static async Task PostRequestAsync(string cmd, string file, string result
/// <param name="cmd">Метод API ("dlanswer" или "dlputanswer").</param>
/// <param name="id">Идентификатор Guid (вида A6563526-A3F3-4D4E-A923-E41E93F1D921)
/// или файл результата отправки запроса, где его можно взять.</param>
/// <param name="resultFile">Имя файла для ответной квитанции.
/// <param name="resultFile">Имя файла для получения информации.
/// Если в конфиге CleanSign: true, то будут очищенный файл и файл.sig в исходном формате PKCS#7.</param>
/// <returns>
/// 200 – результат запроса содержит сведения о среднемесячных платежах Субъекта;
Expand All @@ -176,9 +214,9 @@ public static async Task GetAnswerAsync(string cmd, string id, string resultFile

if (File.Exists(id))
{
XmlDocument xml = new();
xml.Load(id);
id = xml.DocumentElement!.FirstChild!.InnerText;
XmlDocument doc = new();
doc.Load(id);
id = doc.DocumentElement!.FirstChild!.InnerText;
}

if (id.Length != 36) // check Guid
Expand All @@ -188,10 +226,11 @@ public static async Task GetAnswerAsync(string cmd, string id, string resultFile
Environment.Exit(404);
}

while (++retries <= 10)
{
while (++retries <= _config.MaxRetries)
{
using HttpResponseMessage response = await _client.GetAsync(cmd + "?id=" + id);
result = await WriteResultFileAsync(resultFile, response);
(result, _) = await ApiHelper.WriteResultFileAsync(resultFile, response, _config.CleanSign);
response.Dispose();

if (result != 202) break;

Expand All @@ -211,44 +250,4 @@ public static async Task GetAnswerAsync(string cmd, string id, string resultFile

Environment.Exit(result);
}

#endregion public
#region private

/// <summary>
/// Разобрать ответ сервера, записать файл с текстом полученной квитанции и вернуть код результата запроса.
/// </summary>
/// <param name="file">Имя файла для ответной квитанции.
/// Если в конфиге CleanSign: true, то будут очищенный файл и файл.sig в исходном формате PKCS#7.</param>
/// <param name="response">Комплексный ответ сервера.</param>
/// <returns>Код результата запроса, возвращенный сервером.</returns>
/// <exception cref="UnauthorizedAccessException"></exception>
private static async Task<int> WriteResultFileAsync(string file, HttpResponseMessage response)
{
if (File.Exists(file))
{
File.Delete(file);

if (File.Exists(file))
{
throw new UnauthorizedAccessException("Result file not deleted.");
}
}

int result = (int)response.StatusCode;
Console.WriteLine($"Status code: {result} {response.StatusCode}");
byte[] data = await response.Content.ReadAsByteArrayAsync();

if (_config.CleanSign)
{
await File.WriteAllBytesAsync(file + ".sig", data);
data = PKCS7.CleanSign(data);
}

await File.WriteAllBytesAsync(file, data);

return result;
}

#endregion private
}
2 changes: 1 addition & 1 deletion Api5704/Api5704.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<RootNamespace>Api5704</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>8.2024.625</Version>
<Version>8.2024.626</Version>
<Company>diev</Company>
<Copyright>2022-2024 Dmitrii Evdokimov</Copyright>
<Description>Предоставление ССП при обращении пользователя к НБКИ как КБКИ-контрагенту в режиме «одного окна» по Указанию Банка России № 5704-У.</Description>
Expand Down
72 changes: 72 additions & 0 deletions Api5704/ApiExtra.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#region License
/*
Copyright 2022-2024 Dmitrii Evdokimov
Open source software
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion

using System.Text;
using System.Xml;

namespace Api5704;

public static class ApiExtra
{
/// <summary>
/// Пакетная обработка папок с запросами (Extra).
/// </summary>
/// <param name="dir">Папка с исходными запросами.</param>
/// <param name="requests">Папка с готовыми запросами.</param>
/// <param name="results">Папка с готовыми результатами.</param>
/// <param name="answers">Папка с готовыми ответами.</param>
public static async Task PostRequestFolderAsync(string dir, string requests, string results, string answers)
{
Directory.CreateDirectory(requests);
Directory.CreateDirectory(results);
Directory.CreateDirectory(answers);

foreach (var file in Directory.GetFiles(dir, "*.xml"))
{
byte[] data = File.ReadAllBytes(file);

if (data[0] == 0x30)
{
data = PKCS7.CleanSign(data);
}

XmlDocument doc = new();
doc.LoadXml(Encoding.UTF8.GetString(data));
string id = doc.DocumentElement!.GetAttribute("ИдентификаторЗапроса");

string date = DateTime.Now.ToString("yyyy-MM-dd");
string name = $"{date}.{id}";

string request = Path.Combine(results, name + ".request.xml");
string result = Path.Combine(results, name + ".result.xml");
string answer = Path.Combine(answers, name + ".answer.xml");

File.Copy(file, request, true);

await Api.PostRequestAsync(Api.auto, request, result, answer);

if (File.Exists(answer))
{
File.Delete(file);
}

Thread.Sleep(1000);
}
}
}
75 changes: 75 additions & 0 deletions Api5704/ApiHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#region License
/*
Copyright 2022-2024 Dmitrii Evdokimov
Open source software
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#endregion

using System.Text;

namespace Api5704;

public class ApiHelper
{
/// <summary>
/// Разобрать ответ сервера, записать файл с текстом полученной квитанции и вернуть код результата запроса
/// и содержимое квитанции.
/// </summary>
/// <param name="file">Имя файла для ответной квитанции.
/// Если в конфиге CleanSign: true, то будут очищенный файл и файл.sig в исходном формате PKCS#7.</param>
/// <param name="response">Комплексный ответ сервера.</param>
/// <returns>Код результата запроса и содержимое квитанции, возвращенные сервером.</returns>
/// <exception cref="UnauthorizedAccessException"></exception>
public static async Task<(int, string)> WriteResultFileAsync(string file, HttpResponseMessage response, bool clean)
{
if (File.Exists(file))
{
File.Delete(file);

if (File.Exists(file))
{
throw new UnauthorizedAccessException("Result file not deleted.");
}
}

int result = (int)response.StatusCode;
Console.WriteLine($"Status code: {result} {response.StatusCode}");
byte[] data = await response.Content.ReadAsByteArrayAsync();

if (clean)
{
// Write signed file
await File.WriteAllBytesAsync(file + ".sig", data);

// Clean data
data = PKCS7.CleanSign(data);

// Write clean XML
await File.WriteAllBytesAsync(file, data);
}
else
{
// Write signed file
await File.WriteAllBytesAsync(file, data);

// Clean data
data = PKCS7.CleanSign(data);
}

string content = Encoding.UTF8.GetString(data);

return (result, content);
}
}
1 change: 1 addition & 0 deletions Api5704/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class Config
public string? ProxyAddress { get; set; } = "http://192.168.2.1:3128";
public bool SignFile { get; set; } = true;
public bool CleanSign { get; set; } = true;
public int MaxRetries { get; set; } = 10;
public string CspTest { get; set; } =
@"C:\Program Files\Crypto Pro\CSP\csptest.exe";
public string CspTestSignFile { get; set; } =
Expand Down
Loading

0 comments on commit dcd6d00

Please sign in to comment.