DocSpace-client/web/ASC.Web.Core/Files/DocumentService.cs

826 lines
31 KiB
C#
Raw Normal View History

2019-06-07 08:59:07 +00:00
/*
*
* (c) Copyright Ascensio System Limited 2010-2018
*
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
*
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software. If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute.
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
*
*/
namespace ASC.Web.Core.Files
{
/// <summary>
/// Class service connector
/// </summary>
public static class DocumentService
{
/// <summary>
/// Timeout to request conversion
/// </summary>
2022-01-19 14:16:15 +00:00
public static readonly int Timeout = 120000;
//public static int Timeout = Convert.ToInt32(ConfigurationManagerExtension.AppSettings["files.docservice.timeout"] ?? "120000");
2019-06-07 08:59:07 +00:00
/// <summary>
/// Number of tries request conversion
/// </summary>
2022-01-19 14:16:15 +00:00
public static readonly int MaxTry = 3;
2019-06-07 08:59:07 +00:00
/// <summary>
/// Translation key to a supported form.
/// </summary>
/// <param name="expectedKey">Expected key</param>
/// <returns>Supported key</returns>
public static string GenerateRevisionId(string expectedKey)
{
2019-08-15 14:48:55 +00:00
expectedKey ??= "";
2019-06-07 08:59:07 +00:00
const int maxLength = 128;
2019-08-16 08:44:03 +00:00
using var sha256 = SHA256.Create();
if (expectedKey.Length > maxLength) expectedKey = Convert.ToBase64String(sha256.ComputeHash(Encoding.UTF8.GetBytes(expectedKey)));
2019-06-07 08:59:07 +00:00
var key = Regex.Replace(expectedKey, "[^0-9a-zA-Z_]", "_");
return key.Substring(key.Length - Math.Min(key.Length, maxLength));
}
/// <summary>
/// The method is to convert the file to the required format
/// </summary>
/// <param name="documentConverterUrl">Url to the service of conversion</param>
/// <param name="documentUri">Uri for the document to convert</param>
/// <param name="fromExtension">Document extension</param>
/// <param name="toExtension">Extension to which to convert</param>
/// <param name="documentRevisionId">Key for caching on service</param>
/// <param name="password">Password</param>
/// <param name="thumbnail">Thumbnail settings</param>
2019-06-07 08:59:07 +00:00
/// <param name="isAsync">Perform conversions asynchronously</param>
/// <param name="signatureSecret">Secret key to generate the token</param>
/// <param name="convertedDocumentUri">Uri to the converted document</param>
/// <returns>The percentage of completion of conversion</returns>
/// <example>
/// string convertedDocumentUri;
/// GetConvertedUri("http://helpcenter.teamlab.com/content/GettingStarted.pdf", ".pdf", ".docx", "469971047", false, out convertedDocumentUri);
/// </example>
/// <exception>
/// </exception>
2022-03-09 17:15:51 +00:00
2022-02-15 13:20:06 +00:00
public static Task<(int ResultPercent, string ConvertedDocumentUri)> GetConvertedUriAsync(
2019-09-23 12:20:08 +00:00
FileUtility fileUtility,
2019-06-07 08:59:07 +00:00
string documentConverterUrl,
string documentUri,
string fromExtension,
string toExtension,
string documentRevisionId,
string password,
ThumbnailData thumbnail,
SpreadsheetLayout spreadsheetLayout,
2019-06-07 08:59:07 +00:00
bool isAsync,
string signatureSecret,
Merge branch 'develop' into feature/async # Conflicts: # common/ASC.Data.Backup.Core/Core/FileBackupProvider.cs # common/ASC.Data.Backup.Core/Storage/BackupRepository.cs # common/ASC.Data.Backup.Core/Storage/DocumentsBackupStorage.cs # common/ASC.Data.Backup.Core/Tasks/BackupPortalTask.cs # common/ASC.Data.Backup.Core/Tasks/DeletePortalTask.cs # common/ASC.Data.Storage/CrossModuleTransferUtility.cs # common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs # common/ASC.Data.Storage/Extensions.cs # common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs # common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs # common/ASC.Data.Storage/S3/S3Storage.cs # common/ASC.Data.Storage/StorageHandler.cs # common/services/ASC.ApiSystem/Controllers/PortalController.cs # products/ASC.CRM/Server/Api/VoipController.cs # products/ASC.CRM/Server/Core/Dao/RelationshipEventDao.cs # products/ASC.CRM/Server/Utils/PdfCreator.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/TagDao.cs # products/ASC.Files/Core/Core/FileStorageService.cs # products/ASC.Files/Core/Core/Security/FileSecurity.cs # products/ASC.Files/Core/Core/Thirdparty/Box/BoxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Box/BoxFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/CrossDao.cs # products/ASC.Files/Core/Core/Thirdparty/Dropbox/DropboxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Dropbox/DropboxFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveStorage.cs # products/ASC.Files/Core/Core/Thirdparty/IThirdPartyProviderDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveStorage.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderAccountDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderDaoBase.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderSecutiryDao.cs # products/ASC.Files/Core/Core/Thirdparty/SharePoint/SharePointFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/SharePoint/SharePointProviderInfo.cs # products/ASC.Files/Core/Core/Thirdparty/Sharpbox/SharpBoxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Sharpbox/SharpBoxFolderDao.cs # products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs # products/ASC.Files/Core/HttpHandlers/SearchHandler.cs # products/ASC.Files/Core/Services/DocumentService/Configuration.cs # products/ASC.Files/Core/Services/DocumentService/DocumentServiceConnector.cs # products/ASC.Files/Core/Services/DocumentService/DocumentServiceTracker.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileDownloadOperation.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileMoveCopyOperation.cs # products/ASC.Files/Core/ThirdPartyApp/BoxApp.cs # products/ASC.Files/Core/ThirdPartyApp/GoogleDriveApp.cs # products/ASC.Files/Core/Utils/EntryManager.cs # products/ASC.Files/Core/Utils/FileConverter.cs # products/ASC.Files/Core/Utils/FileMarker.cs # products/ASC.Files/Core/Utils/MailMergeTask.cs # products/ASC.Files/Server/Helpers/FilesControllerHelper.cs # web/ASC.Web.Core/Files/DocumentService.cs # web/ASC.Web.Core/Files/DocumentServiceLicense.cs # web/ASC.Web.Core/Helpers/ApiSystemHelper.cs # web/ASC.Web.Core/Recaptcha.cs # web/ASC.Web.Core/Utility/UrlShortener.cs
2022-02-11 21:51:48 +00:00
IHttpClientFactory clientFactory)
2019-06-07 08:59:07 +00:00
{
fromExtension = string.IsNullOrEmpty(fromExtension) ? Path.GetExtension(documentUri) : fromExtension;
2022-01-24 10:49:00 +00:00
if (string.IsNullOrEmpty(fromExtension)) throw new ArgumentNullException(nameof(fromExtension), "Document's extension for conversion is not known");
if (string.IsNullOrEmpty(toExtension)) throw new ArgumentNullException(nameof(toExtension), "Extension for conversion is not known");
2019-06-07 08:59:07 +00:00
2022-02-15 13:20:06 +00:00
return InternalGetConvertedUriAsync(fileUtility, documentConverterUrl, documentUri, fromExtension, toExtension, documentRevisionId, password, thumbnail, spreadsheetLayout, isAsync, signatureSecret, clientFactory);
}
private static async Task<(int ResultPercent, string ConvertedDocumentUri)> InternalGetConvertedUriAsync(
FileUtility fileUtility,
string documentConverterUrl,
string documentUri,
string fromExtension,
string toExtension,
string documentRevisionId,
string password,
ThumbnailData thumbnail,
SpreadsheetLayout spreadsheetLayout,
bool isAsync,
string signatureSecret,
IHttpClientFactory clientFactory)
{
2019-06-07 08:59:07 +00:00
var title = Path.GetFileName(documentUri ?? "");
2022-01-13 12:53:57 +00:00
title = string.IsNullOrEmpty(title) || title.Contains('?') ? Guid.NewGuid().ToString() : title;
2019-06-07 08:59:07 +00:00
documentRevisionId = string.IsNullOrEmpty(documentRevisionId)
? documentUri
: documentRevisionId;
documentRevisionId = GenerateRevisionId(documentRevisionId);
2021-10-12 10:14:33 +00:00
var request = new HttpRequestMessage();
request.RequestUri = new Uri(documentConverterUrl);
request.Method = HttpMethod.Post;
2021-11-24 19:34:39 +00:00
request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
2021-10-12 10:14:33 +00:00
2022-01-13 11:19:39 +00:00
var httpClient = clientFactory.CreateClient();
2021-10-12 10:14:33 +00:00
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout);
2019-06-07 08:59:07 +00:00
var body = new ConvertionBody
2019-08-15 12:04:42 +00:00
{
Async = isAsync,
FileType = fromExtension.Trim('.'),
Key = documentRevisionId,
OutputType = toExtension.Trim('.'),
Title = title,
Thumbnail = thumbnail,
SpreadsheetLayout = spreadsheetLayout,
2019-08-15 12:04:42 +00:00
Url = documentUri,
};
2019-06-07 08:59:07 +00:00
if (!string.IsNullOrEmpty(password))
{
body.Password = password;
}
if (!string.IsNullOrEmpty(signatureSecret))
{
var payload = new Dictionary<string, object>
{
{ "payload", body }
};
2020-04-16 13:07:30 +00:00
var token = JsonWebToken.Encode(payload, signatureSecret);
2019-06-07 08:59:07 +00:00
//todo: remove old scheme
2019-09-23 12:20:08 +00:00
request.Headers.Add(fileUtility.SignatureHeader, "Bearer " + token);
2019-06-07 08:59:07 +00:00
2020-04-16 13:07:30 +00:00
token = JsonWebToken.Encode(body, signatureSecret);
2019-06-07 08:59:07 +00:00
body.Token = token;
}
2021-10-08 09:49:29 +00:00
var bodyString = System.Text.Json.JsonSerializer.Serialize(body, new System.Text.Json.JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
2021-10-08 09:49:29 +00:00
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
2019-06-07 08:59:07 +00:00
2021-11-24 19:34:39 +00:00
request.Content = new StringContent(bodyString, Encoding.UTF8, "application/json");
2019-06-07 08:59:07 +00:00
string dataResponse;
2021-10-12 10:14:33 +00:00
HttpResponseMessage response = null;
2019-06-07 08:59:07 +00:00
Stream responseStream = null;
try
{
var countTry = 0;
while (countTry < MaxTry)
{
try
{
countTry++;
response = await httpClient.SendAsync(request);
responseStream = await response.Content.ReadAsStreamAsync();
2019-06-07 08:59:07 +00:00
break;
}
2021-10-12 10:14:33 +00:00
catch (HttpRequestException ex)
2019-06-07 08:59:07 +00:00
{
2021-11-24 19:34:39 +00:00
throw new HttpException((int)HttpStatusCode.BadRequest, ex.Message, ex);
2019-06-07 08:59:07 +00:00
}
}
if (countTry == MaxTry)
{
2021-10-12 10:14:33 +00:00
throw new HttpRequestException("Timeout");
2019-06-07 08:59:07 +00:00
}
if (responseStream == null) throw new WebException("Could not get an answer");
2019-08-15 15:08:40 +00:00
using var reader = new StreamReader(responseStream);
dataResponse = await reader.ReadToEndAsync();
2019-06-07 08:59:07 +00:00
}
finally
{
if (responseStream != null)
responseStream.Dispose();
if (response != null)
response.Dispose();
}
2022-02-10 18:25:40 +00:00
return GetResponseUri(dataResponse);
2019-06-07 08:59:07 +00:00
}
/// <summary>
/// Request to Document Server with command
/// </summary>
/// <param name="documentTrackerUrl">Url to the command service</param>
/// <param name="method">Name of method</param>
/// <param name="documentRevisionId">Key for caching on service, whose used in editor</param>
/// <param name="callbackUrl">Url to the callback handler</param>
/// <param name="users">users id for drop</param>
/// <param name="meta">file meta data for update</param>
/// <param name="signatureSecret">Secret key to generate the token</param>
/// <param name="version">server version</param>
/// <returns>Response</returns>
public static async Task<CommandResponse> CommandRequestAsync(FileUtility fileUtility,
2019-06-07 08:59:07 +00:00
string documentTrackerUrl,
CommandMethod method,
string documentRevisionId,
string callbackUrl,
string[] users,
MetaData meta,
string signatureSecret,
IHttpClientFactory clientFactory)
2019-06-07 08:59:07 +00:00
{
2021-10-12 10:14:33 +00:00
var request = new HttpRequestMessage();
request.RequestUri = new Uri(documentTrackerUrl);
request.Method = HttpMethod.Post;
2022-01-13 11:19:39 +00:00
var httpClient = clientFactory.CreateClient();
2021-10-12 10:14:33 +00:00
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout);
2019-06-07 08:59:07 +00:00
var body = new CommandBody
2019-08-15 12:04:42 +00:00
{
Command = method,
Key = documentRevisionId,
};
2019-06-07 08:59:07 +00:00
if (!string.IsNullOrEmpty(callbackUrl)) body.Callback = callbackUrl;
if (users != null && users.Length > 0) body.Users = users;
if (meta != null) body.Meta = meta;
if (!string.IsNullOrEmpty(signatureSecret))
{
var payload = new Dictionary<string, object>
2020-04-16 13:07:30 +00:00
{
{ "payload", body }
};
var token = JsonWebToken.Encode(payload, signatureSecret);
2019-06-07 08:59:07 +00:00
//todo: remove old scheme
2019-09-23 12:20:08 +00:00
request.Headers.Add(fileUtility.SignatureHeader, "Bearer " + token);
2019-06-07 08:59:07 +00:00
2020-04-16 13:07:30 +00:00
token = JsonWebToken.Encode(body, signatureSecret);
2019-06-07 08:59:07 +00:00
body.Token = token;
}
2021-10-01 08:04:19 +00:00
var bodyString = System.Text.Json.JsonSerializer.Serialize(body, new System.Text.Json.JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
2021-10-01 08:04:19 +00:00
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
2019-06-07 08:59:07 +00:00
2021-10-12 10:14:33 +00:00
request.Content = new StringContent(bodyString, Encoding.UTF8, "application/json");
2019-06-07 08:59:07 +00:00
string dataResponse;
using (var response = await httpClient.SendAsync(request))
using (var stream = await response.Content.ReadAsStreamAsync())
2019-06-07 08:59:07 +00:00
{
if (stream == null) throw new Exception("Response is null");
2019-08-15 15:08:40 +00:00
using var reader = new StreamReader(stream);
dataResponse = await reader.ReadToEndAsync();
2019-06-07 08:59:07 +00:00
}
try
{
2021-11-16 17:40:15 +00:00
var commandResponse = JsonConvert.DeserializeObject<CommandResponse>(dataResponse);
return commandResponse;
2019-06-07 08:59:07 +00:00
}
2021-11-16 17:40:15 +00:00
catch (Exception ex)
2019-06-07 08:59:07 +00:00
{
2021-11-16 17:40:15 +00:00
return new CommandResponse
{
Error = CommandResponse.ErrorTypes.ParseError,
ErrorString = ex.Message
};
2019-06-07 08:59:07 +00:00
}
}
2022-03-09 17:15:51 +00:00
public static Task<(string DocBuilderKey, Dictionary<string, string> Urls)> DocbuilderRequestAsync(
2020-07-15 14:40:58 +00:00
FileUtility fileUtility,
2019-06-07 08:59:07 +00:00
string docbuilderUrl,
string requestKey,
string scriptUrl,
bool isAsync,
string signatureSecret,
Merge branch 'develop' into feature/async # Conflicts: # common/ASC.Data.Backup.Core/Core/FileBackupProvider.cs # common/ASC.Data.Backup.Core/Storage/BackupRepository.cs # common/ASC.Data.Backup.Core/Storage/DocumentsBackupStorage.cs # common/ASC.Data.Backup.Core/Tasks/BackupPortalTask.cs # common/ASC.Data.Backup.Core/Tasks/DeletePortalTask.cs # common/ASC.Data.Storage/CrossModuleTransferUtility.cs # common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs # common/ASC.Data.Storage/Extensions.cs # common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs # common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs # common/ASC.Data.Storage/S3/S3Storage.cs # common/ASC.Data.Storage/StorageHandler.cs # common/services/ASC.ApiSystem/Controllers/PortalController.cs # products/ASC.CRM/Server/Api/VoipController.cs # products/ASC.CRM/Server/Core/Dao/RelationshipEventDao.cs # products/ASC.CRM/Server/Utils/PdfCreator.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/FileDao.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/FolderDao.cs # products/ASC.Files/Core/Core/Dao/TeamlabDao/TagDao.cs # products/ASC.Files/Core/Core/FileStorageService.cs # products/ASC.Files/Core/Core/Security/FileSecurity.cs # products/ASC.Files/Core/Core/Thirdparty/Box/BoxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Box/BoxFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/CrossDao.cs # products/ASC.Files/Core/Core/Thirdparty/Dropbox/DropboxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Dropbox/DropboxFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/GoogleDrive/GoogleDriveStorage.cs # products/ASC.Files/Core/Core/Thirdparty/IThirdPartyProviderDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/OneDrive/OneDriveStorage.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderAccountDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderDaoBase.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderFolderDao.cs # products/ASC.Files/Core/Core/Thirdparty/ProviderDao/ProviderSecutiryDao.cs # products/ASC.Files/Core/Core/Thirdparty/SharePoint/SharePointFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/SharePoint/SharePointProviderInfo.cs # products/ASC.Files/Core/Core/Thirdparty/Sharpbox/SharpBoxFileDao.cs # products/ASC.Files/Core/Core/Thirdparty/Sharpbox/SharpBoxFolderDao.cs # products/ASC.Files/Core/HttpHandlers/FileHandler.ashx.cs # products/ASC.Files/Core/HttpHandlers/SearchHandler.cs # products/ASC.Files/Core/Services/DocumentService/Configuration.cs # products/ASC.Files/Core/Services/DocumentService/DocumentServiceConnector.cs # products/ASC.Files/Core/Services/DocumentService/DocumentServiceTracker.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileDownloadOperation.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs # products/ASC.Files/Core/Services/WCFService/FileOperations/FileMoveCopyOperation.cs # products/ASC.Files/Core/ThirdPartyApp/BoxApp.cs # products/ASC.Files/Core/ThirdPartyApp/GoogleDriveApp.cs # products/ASC.Files/Core/Utils/EntryManager.cs # products/ASC.Files/Core/Utils/FileConverter.cs # products/ASC.Files/Core/Utils/FileMarker.cs # products/ASC.Files/Core/Utils/MailMergeTask.cs # products/ASC.Files/Server/Helpers/FilesControllerHelper.cs # web/ASC.Web.Core/Files/DocumentService.cs # web/ASC.Web.Core/Files/DocumentServiceLicense.cs # web/ASC.Web.Core/Helpers/ApiSystemHelper.cs # web/ASC.Web.Core/Recaptcha.cs # web/ASC.Web.Core/Utility/UrlShortener.cs
2022-02-11 21:51:48 +00:00
IHttpClientFactory clientFactory)
2019-06-07 08:59:07 +00:00
{
2022-03-09 17:15:51 +00:00
ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(docbuilderUrl);
2019-06-07 08:59:07 +00:00
if (string.IsNullOrEmpty(requestKey) && string.IsNullOrEmpty(scriptUrl))
throw new ArgumentException("requestKey or inputScript is empty");
2022-02-15 13:20:06 +00:00
return InternalDocbuilderRequestAsync(fileUtility, docbuilderUrl, requestKey, scriptUrl, isAsync, signatureSecret, clientFactory);
}
private static async Task<(string DocBuilderKey, Dictionary<string, string> Urls)> InternalDocbuilderRequestAsync(
FileUtility fileUtility,
string docbuilderUrl,
string requestKey,
string scriptUrl,
bool isAsync,
string signatureSecret,
IHttpClientFactory clientFactory)
{
2021-10-12 10:14:33 +00:00
var request = new HttpRequestMessage();
request.RequestUri = new Uri(docbuilderUrl);
request.Method = HttpMethod.Post;
2021-10-21 14:56:31 +00:00
2022-01-13 11:19:39 +00:00
var httpClient = clientFactory.CreateClient();
2021-10-12 10:14:33 +00:00
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout);
2019-06-07 08:59:07 +00:00
var body = new BuilderBody
2019-08-15 12:04:42 +00:00
{
Async = isAsync,
Key = requestKey,
Url = scriptUrl
};
2019-06-07 08:59:07 +00:00
if (!string.IsNullOrEmpty(signatureSecret))
{
var payload = new Dictionary<string, object>
{
{ "payload", body }
};
2020-04-16 13:07:30 +00:00
var token = JsonWebToken.Encode(payload, signatureSecret);
2019-06-07 08:59:07 +00:00
//todo: remove old scheme
2019-09-23 12:20:08 +00:00
request.Headers.Add(fileUtility.SignatureHeader, "Bearer " + token);
2019-06-07 08:59:07 +00:00
2020-04-16 13:07:30 +00:00
token = JsonWebToken.Encode(body, signatureSecret);
2019-06-07 08:59:07 +00:00
body.Token = token;
}
2021-10-08 09:49:29 +00:00
var bodyString = System.Text.Json.JsonSerializer.Serialize(body, new System.Text.Json.JsonSerializerOptions()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
2021-10-08 09:49:29 +00:00
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
});
2019-06-07 08:59:07 +00:00
2021-10-12 10:14:33 +00:00
request.Content = new StringContent(bodyString, Encoding.UTF8, "application/json");
2019-06-07 08:59:07 +00:00
string dataResponse = null;
2021-10-12 10:14:33 +00:00
using (var response = await httpClient.SendAsync(request))
using (var responseStream = await response.Content.ReadAsStreamAsync())
2019-06-07 08:59:07 +00:00
{
if (responseStream != null)
{
2019-08-15 15:08:40 +00:00
using var reader = new StreamReader(responseStream);
dataResponse = await reader.ReadToEndAsync();
2019-06-07 08:59:07 +00:00
}
}
if (string.IsNullOrEmpty(dataResponse)) throw new Exception("Invalid response");
var responseFromService = JObject.Parse(dataResponse);
if (responseFromService == null) throw new Exception("Invalid answer format");
var errorElement = responseFromService.Value<string>("error");
2020-07-15 14:40:58 +00:00
if (!string.IsNullOrEmpty(errorElement))
{
DocumentServiceException.ProcessResponseError(errorElement);
}
2019-06-07 08:59:07 +00:00
var isEnd = responseFromService.Value<bool>("end");
Dictionary<string, string> urls = null;
2019-06-07 08:59:07 +00:00
if (isEnd)
{
IDictionary<string, JToken> rates = (JObject)responseFromService["urls"];
urls = rates.ToDictionary(pair => pair.Key, pair => pair.Value.ToString());
}
return (responseFromService.Value<string>("key"), urls);
2019-06-07 08:59:07 +00:00
}
2022-02-15 13:20:06 +00:00
public static Task<bool> HealthcheckRequestAsync(string healthcheckUrl, IHttpClientFactory clientFactory)
2019-06-07 08:59:07 +00:00
{
2022-03-09 17:15:51 +00:00
ArgumentNullOrEmptyException.ThrowIfNullOrEmpty(healthcheckUrl);
2019-06-07 08:59:07 +00:00
2022-02-15 13:20:06 +00:00
return InternalHealthcheckRequestAsync(healthcheckUrl, clientFactory);
}
private static async Task<bool> InternalHealthcheckRequestAsync(string healthcheckUrl, IHttpClientFactory clientFactory)
{
2021-10-12 10:14:33 +00:00
var request = new HttpRequestMessage();
request.RequestUri = new Uri(healthcheckUrl);
2022-01-13 11:19:39 +00:00
var httpClient = clientFactory.CreateClient();
2021-10-12 10:14:33 +00:00
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout);
2019-06-07 08:59:07 +00:00
using var response = await httpClient.SendAsync(request);
using var responseStream = await response.Content.ReadAsStreamAsync();
2019-08-15 15:08:40 +00:00
if (responseStream == null)
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:08:40 +00:00
throw new Exception("Empty response");
2019-06-07 08:59:07 +00:00
}
2019-08-15 15:08:40 +00:00
using var reader = new StreamReader(responseStream);
var dataResponse = await reader.ReadToEndAsync();
2019-08-15 15:08:40 +00:00
return dataResponse.Equals("true", StringComparison.InvariantCultureIgnoreCase);
2019-06-07 08:59:07 +00:00
}
public enum CommandMethod
{
Info,
Drop,
Saved, //not used
Version,
ForceSave, //not used
Meta,
2021-11-16 17:40:15 +00:00
License
2019-06-07 08:59:07 +00:00
}
2021-11-16 17:40:15 +00:00
[Serializable]
[DebuggerDisplay("{Key}")]
public class CommandResponse
2019-06-07 08:59:07 +00:00
{
2021-11-16 17:40:15 +00:00
[JsonPropertyName("error")]
public ErrorTypes Error { get; set; }
[JsonPropertyName("errorString")]
public string ErrorString { get; set; }
[JsonPropertyName("key")]
public string Key { get; set; }
[JsonPropertyName("license")]
public License License { get; set; }
[JsonPropertyName("server")]
public ServerInfo Server { get; set; }
[JsonPropertyName("quota")]
public QuotaInfo Quota { get; set; }
[JsonPropertyName("version")]
public string Version { get; set; }
public enum ErrorTypes
{
NoError = 0,
DocumentIdError = 1,
ParseError = 2,
UnknownError = 3,
NotModify = 4,
UnknownCommand = 5,
Token = 6,
TokenExpire = 7,
}
[Serializable]
[DebuggerDisplay("{BuildVersion}")]
public class ServerInfo
{
[JsonPropertyName("buildDate")]
public DateTime BuildDate { get; set; }
[JsonPropertyName("buildNumber")]
public int buildNumber { get; set; }
[JsonPropertyName("buildVersion")]
public string BuildVersion { get; set; }
[JsonPropertyName("packageType")]
public PackageTypes PackageType { get; set; }
[JsonPropertyName("resultType")]
public ResultTypes ResultType { get; set; }
[JsonPropertyName("workersCount")]
public int WorkersCount { get; set; }
public enum PackageTypes
{
OpenSource = 0,
IntegrationEdition = 1,
DeveloperEdition = 2
}
public enum ResultTypes
{
Error = 1,
Expired = 2,
Success = 3,
UnknownUser = 4,
Connections = 5,
ExpiredTrial = 6,
SuccessLimit = 7,
UsersCount = 8,
ConnectionsOS = 9,
UsersCountOS = 10,
ExpiredLimited = 11
}
}
[Serializable]
[DataContract(Name = "Quota", Namespace = "")]
public class QuotaInfo
{
[JsonPropertyName("users")]
public List<User> Users { get; set; }
[Serializable]
[DebuggerDisplay("{UserId} ({Expire})")]
public class User
{
[JsonPropertyName("userid")]
public string UserId { get; set; }
[JsonPropertyName("expire")]
public DateTime Expire { get; set; }
}
}
2019-06-07 08:59:07 +00:00
}
[Serializable]
[DebuggerDisplay("{Command} ({Key})")]
private class CommandBody
{
2020-07-15 21:08:28 +00:00
[System.Text.Json.Serialization.JsonIgnore]
2019-06-07 08:59:07 +00:00
public CommandMethod Command { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("c")]
2019-06-07 08:59:07 +00:00
public string C
{
get { return Command.ToString().ToLower(CultureInfo.InvariantCulture); }
}
2020-07-15 14:40:58 +00:00
2020-07-15 21:25:19 +00:00
[JsonPropertyName("callback")]
2019-06-07 08:59:07 +00:00
public string Callback { get; set; }
2020-07-15 14:40:58 +00:00
2020-07-15 21:25:19 +00:00
[JsonPropertyName("key")]
2019-06-07 08:59:07 +00:00
public string Key { get; set; }
2020-07-15 14:40:58 +00:00
2020-07-15 21:25:19 +00:00
[JsonPropertyName("meta")]
2019-06-07 08:59:07 +00:00
public MetaData Meta { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("users")]
2019-06-07 08:59:07 +00:00
public string[] Users { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("token")]
2019-06-07 08:59:07 +00:00
public string Token { get; set; }
//not used
2020-07-15 21:25:19 +00:00
[JsonPropertyName("userdata")]
2019-06-07 08:59:07 +00:00
public string UserData { get; set; }
}
[Serializable]
[DebuggerDisplay("{Title}")]
public class MetaData
{
2020-07-15 21:25:19 +00:00
[JsonPropertyName("title")]
2020-07-15 21:08:28 +00:00
public string Title { get; set; }
2019-06-07 08:59:07 +00:00
}
[Serializable]
[DebuggerDisplay("{Height}x{Width}")]
public class ThumbnailData
{
[JsonPropertyName("aspect")]
public int Aspect { get; set; }
[JsonPropertyName("first")]
public bool First { get; set; }
[JsonPropertyName("height")]
public int Height { get; set; }
[JsonPropertyName("width")]
public int Width { get; set; }
}
[Serializable]
[DataContract(Name = "spreadsheetLayout", Namespace = "")]
[DebuggerDisplay("SpreadsheetLayout {IgnorePrintArea} {Orientation} {FitToHeight} {FitToWidth} {Headings} {GridLines}")]
public class SpreadsheetLayout
{
[JsonPropertyName("ignorePrintArea")]
public bool IgnorePrintArea { get; set; }
[JsonPropertyName("orientation")]
public string Orientation { get; set; }
[JsonPropertyName("fitToHeight")]
public int FitToHeight { get; set; }
[JsonPropertyName("fitToWidth")]
public int FitToWidth { get; set; }
[JsonPropertyName("headings")]
public bool Headings { get; set; }
[JsonPropertyName("gridLines")]
public bool GridLines { get; set; }
[JsonPropertyName("margins")]
2021-11-16 17:40:15 +00:00
public LayoutMargins Margins { get; set; }
[JsonPropertyName("pageSize")]
2021-11-16 17:40:15 +00:00
public LayoutPageSize PageSize { get; set; }
2021-11-16 17:40:15 +00:00
[Serializable]
[DebuggerDisplay("Margins {Top} {Right} {Bottom} {Left}")]
public class LayoutMargins
{
[JsonPropertyName("left")]
public string Left { get; set; }
2021-11-16 17:40:15 +00:00
[JsonPropertyName("right")]
public string Right { get; set; }
2021-11-16 17:40:15 +00:00
[JsonPropertyName("top")]
public string Top { get; set; }
2021-11-16 17:40:15 +00:00
[JsonPropertyName("bottom")]
public string Bottom { get; set; }
}
2021-11-16 17:40:15 +00:00
[Serializable]
[DebuggerDisplay("PageSize {Width} {Height}")]
public class LayoutPageSize
{
[JsonPropertyName("height")]
public string Height { get; set; }
[JsonPropertyName("width")]
public string Width { get; set; }
}
}
2019-06-07 08:59:07 +00:00
[Serializable]
[DebuggerDisplay("{Title} from {FileType} to {OutputType} ({Key})")]
private class ConvertionBody
{
2020-07-15 21:25:19 +00:00
[JsonPropertyName("async")]
2019-06-07 08:59:07 +00:00
public bool Async { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("filetype")]
2019-06-07 08:59:07 +00:00
public string FileType { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("key")]
2019-06-07 08:59:07 +00:00
public string Key { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("outputtype")]
2019-06-07 08:59:07 +00:00
public string OutputType { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("password")]
2019-06-07 08:59:07 +00:00
public string Password { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("title")]
2019-06-07 08:59:07 +00:00
public string Title { get; set; }
[JsonPropertyName("thumbnail")]
public ThumbnailData Thumbnail { get; set; }
[JsonPropertyName("spreadsheetLayout")]
public SpreadsheetLayout SpreadsheetLayout { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("url")]
2019-06-07 08:59:07 +00:00
public string Url { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("token")]
2019-06-07 08:59:07 +00:00
public string Token { get; set; }
}
[Serializable]
[DebuggerDisplay("{Key}")]
private class BuilderBody
{
2020-07-15 21:25:19 +00:00
[JsonPropertyName("async")]
2019-06-07 08:59:07 +00:00
public bool Async { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("key")]
2019-06-07 08:59:07 +00:00
public string Key { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("url")]
2019-06-07 08:59:07 +00:00
public string Url { get; set; }
2020-07-15 21:25:19 +00:00
[JsonPropertyName("token")]
2019-06-07 08:59:07 +00:00
public string Token { get; set; }
}
[Serializable]
public class FileLink
{
2020-07-15 21:25:19 +00:00
[JsonPropertyName("filetype")]
public string FileType { get; set; }
2019-06-07 08:59:07 +00:00
2020-07-15 21:25:19 +00:00
[JsonPropertyName("token")]
public string Token { get; set; }
2019-06-07 08:59:07 +00:00
2020-07-15 21:25:19 +00:00
[JsonPropertyName("url")]
public string Url { get; set; }
2019-06-07 08:59:07 +00:00
}
[Serializable]
2019-06-07 08:59:07 +00:00
public class DocumentServiceException : Exception
{
2022-01-21 09:19:57 +00:00
public ErrorCode Code { get; set; }
2019-06-07 08:59:07 +00:00
public DocumentServiceException(ErrorCode errorCode, string message)
: base(message)
{
Code = errorCode;
}
protected DocumentServiceException(SerializationInfo info, StreamingContext context) : base(info, context)
{
if (info != null)
{
Code = (ErrorCode)info.GetValue("Code", typeof(ErrorCode));
}
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
if (info != null)
{
info.AddValue("Code", Code);
}
}
2019-06-07 08:59:07 +00:00
public static void ProcessResponseError(string errorCode)
{
2019-08-15 13:05:50 +00:00
if (!Enum.TryParse(errorCode, true, out ErrorCode code))
2019-06-07 08:59:07 +00:00
{
code = ErrorCode.Unknown;
}
2019-08-15 15:13:25 +00:00
var errorMessage = code switch
2019-06-07 08:59:07 +00:00
{
2019-08-15 15:13:25 +00:00
ErrorCode.VkeyUserCountExceed => "user count exceed",
ErrorCode.VkeyKeyExpire => "signature expire",
ErrorCode.VkeyEncrypt => "encrypt signature",
ErrorCode.UploadCountFiles => "count files",
ErrorCode.UploadExtension => "extension",
ErrorCode.UploadContentLength => "upload length",
ErrorCode.Vkey => "document signature",
ErrorCode.TaskQueue => "database",
ErrorCode.ConvertPassword => "password",
ErrorCode.ConvertDownload => "download",
ErrorCode.Convert => "convertation",
ErrorCode.ConvertTimeout => "convertation timeout",
ErrorCode.Unknown => "unknown error",
_ => "errorCode = " + errorCode,
};
2019-06-07 08:59:07 +00:00
throw new DocumentServiceException(code, errorMessage);
}
public enum ErrorCode
{
VkeyUserCountExceed = -22,
VkeyKeyExpire = -21,
VkeyEncrypt = -20,
UploadCountFiles = -11,
UploadExtension = -10,
UploadContentLength = -9,
Vkey = -8,
TaskQueue = -6,
ConvertPassword = -5,
ConvertDownload = -4,
Convert = -3,
ConvertTimeout = -2,
Unknown = -1
}
}
/// <summary>
/// Processing document received from the editing service
/// </summary>
/// <param name="jsonDocumentResponse">The resulting json from editing service</param>
/// <param name="responseUri">Uri to the converted document</param>
/// <returns>The percentage of completion of conversion</returns>
2022-02-10 18:25:40 +00:00
private static (int ResultPercent, string responseuri) GetResponseUri(string jsonDocumentResponse)
2019-06-07 08:59:07 +00:00
{
2022-01-24 10:49:00 +00:00
if (string.IsNullOrEmpty(jsonDocumentResponse)) throw new ArgumentException("Invalid param", nameof(jsonDocumentResponse));
2019-06-07 08:59:07 +00:00
var responseFromService = JObject.Parse(jsonDocumentResponse);
if (responseFromService == null) throw new WebException("Invalid answer format");
var errorElement = responseFromService.Value<string>("error");
2020-07-15 21:49:25 +00:00
if (!string.IsNullOrEmpty(errorElement)) DocumentServiceException.ProcessResponseError(errorElement);
2019-06-07 08:59:07 +00:00
var isEndConvert = responseFromService.Value<bool>("endConvert");
int resultPercent;
2022-02-10 18:25:40 +00:00
var responseUri = string.Empty;
2019-06-07 08:59:07 +00:00
if (isEndConvert)
{
responseUri = responseFromService.Value<string>("fileUrl");
resultPercent = 100;
}
else
{
resultPercent = responseFromService.Value<int>("percent");
if (resultPercent >= 100) resultPercent = 99;
}
2022-02-10 18:25:40 +00:00
return (resultPercent, responseUri);
2019-06-07 08:59:07 +00:00
}
}
}