2020-01-27 11:15:18 +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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Net;
|
|
|
|
using System.Threading;
|
2020-02-05 13:33:09 +00:00
|
|
|
using System.Threading.Tasks;
|
2020-01-27 11:15:18 +00:00
|
|
|
using System.Web;
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
using ASC.Common;
|
2020-02-05 13:33:09 +00:00
|
|
|
using ASC.Common.Logging;
|
2020-01-27 11:15:18 +00:00
|
|
|
using ASC.Common.Web;
|
|
|
|
using ASC.Core;
|
|
|
|
using ASC.Files.Core;
|
2020-04-13 11:46:26 +00:00
|
|
|
using ASC.Files.Core.Data;
|
2020-02-05 13:33:09 +00:00
|
|
|
using ASC.Files.Core.Security;
|
2020-02-18 08:39:28 +00:00
|
|
|
using ASC.Files.Resources;
|
2020-01-27 11:15:18 +00:00
|
|
|
using ASC.MessagingSystem;
|
|
|
|
using ASC.Security.Cryptography;
|
|
|
|
using ASC.Web.Core;
|
|
|
|
using ASC.Web.Core.Files;
|
|
|
|
using ASC.Web.Files.Classes;
|
|
|
|
using ASC.Web.Files.Core;
|
|
|
|
using ASC.Web.Files.Helpers;
|
|
|
|
using ASC.Web.Files.Services.DocumentService;
|
|
|
|
using ASC.Web.Files.Services.FFmpegService;
|
|
|
|
using ASC.Web.Files.Utils;
|
|
|
|
using ASC.Web.Studio.Core;
|
|
|
|
using ASC.Web.Studio.UserControls.Statistics;
|
2020-02-05 13:33:09 +00:00
|
|
|
using ASC.Web.Studio.Utility;
|
|
|
|
|
2020-01-27 11:15:18 +00:00
|
|
|
using JWT;
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
using Microsoft.AspNetCore.Builder;
|
2020-02-05 13:33:09 +00:00
|
|
|
using Microsoft.AspNetCore.Http;
|
2020-02-10 16:04:54 +00:00
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2020-02-05 13:33:09 +00:00
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
2020-01-27 11:15:18 +00:00
|
|
|
using Newtonsoft.Json.Linq;
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-01-27 11:15:18 +00:00
|
|
|
using FileShare = ASC.Files.Core.Security.FileShare;
|
|
|
|
using MimeMapping = ASC.Common.Web.MimeMapping;
|
|
|
|
using SecurityContext = ASC.Core.SecurityContext;
|
|
|
|
|
|
|
|
namespace ASC.Web.Files
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
public class FileHandler
|
|
|
|
{
|
|
|
|
public RequestDelegate Next { get; }
|
|
|
|
public IServiceProvider ServiceProvider { get; }
|
|
|
|
|
|
|
|
public FileHandler(RequestDelegate next, IServiceProvider serviceProvider)
|
|
|
|
{
|
|
|
|
Next = next;
|
|
|
|
ServiceProvider = serviceProvider;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task Invoke(HttpContext context)
|
|
|
|
{
|
|
|
|
using var scope = ServiceProvider.CreateScope();
|
|
|
|
var fileHandlerService = scope.ServiceProvider.GetService<FileHandlerService>();
|
|
|
|
await fileHandlerService.Invoke(context);
|
|
|
|
await Next.Invoke(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class FileHandlerService
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
public string FileHandlerPath
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
get { return FilesLinkUtility.FileHandlerPath; }
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
public FilesLinkUtility FilesLinkUtility { get; }
|
|
|
|
public TenantExtra TenantExtra { get; }
|
|
|
|
public AuthContext AuthContext { get; }
|
|
|
|
public SecurityContext SecurityContext { get; }
|
|
|
|
public GlobalStore GlobalStore { get; }
|
|
|
|
public IDaoFactory DaoFactory { get; }
|
|
|
|
public FileSecurity FileSecurity { get; }
|
|
|
|
public FileMarker FileMarker { get; }
|
|
|
|
public SetupInfo SetupInfo { get; }
|
|
|
|
public FileUtility FileUtility { get; }
|
|
|
|
public Global Global { get; }
|
|
|
|
public EmailValidationKeyProvider EmailValidationKeyProvider { get; }
|
|
|
|
public CoreBaseSettings CoreBaseSettings { get; }
|
|
|
|
public GlobalFolderHelper GlobalFolderHelper { get; }
|
|
|
|
public PathProvider PathProvider { get; }
|
|
|
|
public DocumentServiceTrackerHelper DocumentServiceTrackerHelper { get; }
|
2020-02-05 15:37:31 +00:00
|
|
|
public FilesMessageService FilesMessageService { get; }
|
2020-02-06 11:55:11 +00:00
|
|
|
public FileShareLink FileShareLink { get; }
|
|
|
|
public FileConverter FileConverter { get; }
|
2020-02-10 16:04:54 +00:00
|
|
|
public FFmpegService FFmpegService { get; }
|
|
|
|
public IServiceProvider ServiceProvider { get; }
|
2020-02-05 13:33:09 +00:00
|
|
|
public UserManager UserManager { get; }
|
|
|
|
public ILog Logger { get; }
|
|
|
|
public CookiesManager CookiesManager { get; }
|
|
|
|
public TenantStatisticsProvider TenantStatisticsProvider { get; }
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
public FileHandlerService(
|
2020-02-05 13:33:09 +00:00
|
|
|
FilesLinkUtility filesLinkUtility,
|
|
|
|
TenantExtra tenantExtra,
|
|
|
|
CookiesManager cookiesManager,
|
|
|
|
AuthContext authContext,
|
|
|
|
SecurityContext securityContext,
|
|
|
|
GlobalStore globalStore,
|
|
|
|
IOptionsMonitor<ILog> optionsMonitor,
|
|
|
|
IDaoFactory daoFactory,
|
|
|
|
FileSecurity fileSecurity,
|
|
|
|
FileMarker fileMarker,
|
|
|
|
SetupInfo setupInfo,
|
|
|
|
FileUtility fileUtility,
|
|
|
|
Global global,
|
|
|
|
EmailValidationKeyProvider emailValidationKeyProvider,
|
|
|
|
CoreBaseSettings coreBaseSettings,
|
|
|
|
GlobalFolderHelper globalFolderHelper,
|
|
|
|
PathProvider pathProvider,
|
|
|
|
UserManager userManager,
|
2020-02-05 15:37:31 +00:00
|
|
|
DocumentServiceTrackerHelper documentServiceTrackerHelper,
|
2020-02-06 11:55:11 +00:00
|
|
|
FilesMessageService filesMessageService,
|
|
|
|
FileShareLink fileShareLink,
|
2020-02-10 16:04:54 +00:00
|
|
|
FileConverter fileConverter,
|
|
|
|
FFmpegService fFmpegService,
|
|
|
|
IServiceProvider serviceProvider)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
FilesLinkUtility = filesLinkUtility;
|
|
|
|
TenantExtra = tenantExtra;
|
|
|
|
AuthContext = authContext;
|
|
|
|
SecurityContext = securityContext;
|
|
|
|
GlobalStore = globalStore;
|
|
|
|
DaoFactory = daoFactory;
|
|
|
|
FileSecurity = fileSecurity;
|
|
|
|
FileMarker = fileMarker;
|
|
|
|
SetupInfo = setupInfo;
|
|
|
|
FileUtility = fileUtility;
|
|
|
|
Global = global;
|
|
|
|
EmailValidationKeyProvider = emailValidationKeyProvider;
|
|
|
|
CoreBaseSettings = coreBaseSettings;
|
|
|
|
GlobalFolderHelper = globalFolderHelper;
|
|
|
|
PathProvider = pathProvider;
|
|
|
|
DocumentServiceTrackerHelper = documentServiceTrackerHelper;
|
2020-02-05 15:37:31 +00:00
|
|
|
FilesMessageService = filesMessageService;
|
2020-02-06 11:55:11 +00:00
|
|
|
FileShareLink = fileShareLink;
|
|
|
|
FileConverter = fileConverter;
|
2020-02-10 16:04:54 +00:00
|
|
|
FFmpegService = fFmpegService;
|
|
|
|
ServiceProvider = serviceProvider;
|
2020-02-05 13:33:09 +00:00
|
|
|
UserManager = userManager;
|
|
|
|
Logger = optionsMonitor.CurrentValue;
|
|
|
|
CookiesManager = cookiesManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task Invoke(HttpContext context)
|
|
|
|
{
|
|
|
|
if (TenantExtra.IsNotPaid())
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.PaymentRequired;
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.StatusDescription = "Payment Required.";
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
switch ((context.Request.Query[FilesLinkUtility.Action].FirstOrDefault() ?? "").ToLower())
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
case "view":
|
|
|
|
case "download":
|
2020-04-13 11:46:26 +00:00
|
|
|
await DownloadFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "bulk":
|
2020-04-13 11:46:26 +00:00
|
|
|
await BulkDownloadFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "stream":
|
2020-04-13 11:46:26 +00:00
|
|
|
await StreamFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "empty":
|
2020-04-13 11:46:26 +00:00
|
|
|
await EmptyFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "tmp":
|
2020-04-13 11:46:26 +00:00
|
|
|
await TempFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "create":
|
2020-04-13 11:46:26 +00:00
|
|
|
await CreateFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "redirect":
|
|
|
|
Redirect(context);
|
|
|
|
break;
|
|
|
|
case "diff":
|
2020-04-13 11:46:26 +00:00
|
|
|
await DifferenceFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
case "track":
|
2020-04-13 11:46:26 +00:00
|
|
|
await TrackFile(context);
|
2020-01-27 11:15:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
catch (InvalidOperationException e)
|
|
|
|
{
|
|
|
|
throw new HttpException((int)HttpStatusCode.InternalServerError, FilesCommonResource.ErrorMassage_BadRequest, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task BulkDownloadFile(HttpContext context)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
if (!SecurityContext.AuthenticateMe(CookiesManager.GetCookies(CookiesType.AuthKey)))
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var store = GlobalStore.GetStore();
|
|
|
|
var path = string.Format(@"{0}\{1}.zip", AuthContext.CurrentAccount.ID, FileConstant.DownloadTitle);
|
2020-01-27 11:15:18 +00:00
|
|
|
if (!store.IsFile(FileConstant.StorageDomainTmp, path))
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("BulkDownload file error. File is not exist on storage. UserId: {0}.", AuthContext.CurrentAccount.ID);
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (store.IsSupportedPreSignedUri)
|
|
|
|
{
|
|
|
|
var url = store.GetPreSignedUri(FileConstant.StorageDomainTmp, path, TimeSpan.FromHours(1), null).ToString();
|
|
|
|
context.Response.Redirect(url);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.Response.Clear();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var flushed = false;
|
2020-01-27 11:15:18 +00:00
|
|
|
using (var readStream = store.GetReadStream(FileConstant.StorageDomainTmp, path))
|
|
|
|
{
|
|
|
|
long offset = 0;
|
2020-02-05 13:33:09 +00:00
|
|
|
var length = readStream.Length;
|
2020-01-27 11:15:18 +00:00
|
|
|
if (readStream.CanSeek)
|
|
|
|
{
|
|
|
|
length = ProcessRangeHeader(context, readStream.Length, ref offset);
|
|
|
|
readStream.Seek(offset, SeekOrigin.Begin);
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
flushed = await SendStreamByChunksAsync(context, length, FileConstant.DownloadTitle + ".zip", readStream, flushed);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("BulkDownloadFile failed for user {0} with error: ", SecurityContext.CurrentAccount.ID, e.Message);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task DownloadFile(HttpContext context)
|
2020-03-02 12:31:53 +00:00
|
|
|
{
|
|
|
|
var q = context.Request.Query[FilesLinkUtility.FileId];
|
|
|
|
|
|
|
|
if (int.TryParse(q, out var id))
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await DownloadFile(context, id);
|
2020-03-02 12:31:53 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await DownloadFile(context, q);
|
2020-03-02 12:31:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task DownloadFile<T>(HttpContext context, T id)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
var flushed = false;
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var doc = context.Request.Query[FilesLinkUtility.DocShareKey].FirstOrDefault() ?? "";
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-02 12:31:53 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
var readLink = FileShareLink.Check(doc, true, fileDao, out var file);
|
|
|
|
if (!readLink && file == null)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
fileDao.InvalidateCache(id);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
file = int.TryParse(context.Request.Query[FilesLinkUtility.Version], out var version) && version > 0
|
|
|
|
? fileDao.GetFile(id, version)
|
|
|
|
: fileDao.GetFile(id);
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file == null)
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
if (!readLink && !FileSecurity.CanRead(file))
|
2020-02-05 13:33:09 +00:00
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!string.IsNullOrEmpty(file.Error)) throw new Exception(file.Error);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!fileDao.IsExistOnStorage(file))
|
|
|
|
{
|
|
|
|
Logger.ErrorFormat("Download file error. File is not exist on storage. File id: {0}.", file.ID);
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
FileMarker.RemoveMarkAsNew(file);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Clear();
|
|
|
|
context.Response.Headers.Clear();
|
|
|
|
//TODO
|
|
|
|
//context.Response.Headers.Charset = "utf-8";
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 15:37:31 +00:00
|
|
|
FilesMessageService.Send(file, MessageAction.FileDownloaded, file.Title);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (string.Equals(context.Request.Headers["If-None-Match"], GetEtag(file)))
|
|
|
|
{
|
|
|
|
//Its cached. Reply 304
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotModified;
|
|
|
|
//context.Response.Cache.SetETag(GetEtag(file));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//context.Response.CacheControl = "public";
|
|
|
|
//context.Response.Cache.SetETag(GetEtag(file));
|
|
|
|
//context.Response.Cache.SetCacheability(HttpCacheability.Public);
|
|
|
|
|
|
|
|
Stream fileStream = null;
|
|
|
|
try
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var title = file.Title;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file.ContentLength <= SetupInfo.AvailableFileSize)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var ext = FileUtility.GetFileExtension(file.Title);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var outType = (context.Request.Query[FilesLinkUtility.OutType].FirstOrDefault() ?? "").Trim();
|
|
|
|
if (!string.IsNullOrEmpty(outType)
|
|
|
|
&& FileUtility.ExtsConvertible.Keys.Contains(ext)
|
|
|
|
&& FileUtility.ExtsConvertible[ext].Contains(outType))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
ext = outType;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
long offset = 0;
|
|
|
|
long length;
|
|
|
|
if (!file.ProviderEntry
|
|
|
|
&& string.Equals(context.Request.Query["convpreview"], "true", StringComparison.InvariantCultureIgnoreCase)
|
|
|
|
&& FFmpegService.IsConvertable(ext))
|
|
|
|
{
|
|
|
|
const string mp4Name = "content.mp4";
|
2020-03-02 12:31:53 +00:00
|
|
|
var mp4Path = fileDao.GetUniqFilePath(file, mp4Name);
|
2020-02-05 13:33:09 +00:00
|
|
|
var store = GlobalStore.GetStore();
|
|
|
|
if (!store.IsFile(mp4Path))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
fileStream = fileDao.GetFileStream(file);
|
|
|
|
|
|
|
|
Logger.InfoFormat("Converting {0} (fileId: {1}) to mp4", file.Title, file.ID);
|
|
|
|
var stream = FFmpegService.Convert(fileStream, ext);
|
|
|
|
store.Save(string.Empty, mp4Path, stream, mp4Name);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var fullLength = store.GetFileSize(string.Empty, mp4Path);
|
|
|
|
|
|
|
|
length = ProcessRangeHeader(context, fullLength, ref offset);
|
|
|
|
fileStream = store.GetReadStream(string.Empty, mp4Path, (int)offset);
|
|
|
|
|
|
|
|
title = FileUtility.ReplaceFileExtension(title, ".mp4");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!FileConverter.EnableConvert(file, ext))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!readLink && fileDao.IsSupportedPreSignedUri(file))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Redirect(fileDao.GetPreSignedUri(file, TimeSpan.FromHours(1)).ToString(), true);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
fileStream = fileDao.GetFileStream(file); // getStream to fix file.ContentLength
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (fileStream.CanSeek)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var fullLength = file.ContentLength;
|
|
|
|
length = ProcessRangeHeader(context, fullLength, ref offset);
|
|
|
|
fileStream.Seek(offset, SeekOrigin.Begin);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
length = file.ContentLength;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
else
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
title = FileUtility.ReplaceFileExtension(title, ext);
|
|
|
|
fileStream = FileConverter.Exec(file, ext);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
length = fileStream.Length;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
flushed = await SendStreamByChunksAsync(context, length, title, fileStream, flushed);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
else
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!readLink && fileDao.IsSupportedPreSignedUri(file))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Redirect(fileDao.GetPreSignedUri(file, TimeSpan.FromHours(1)).ToString(), true);
|
|
|
|
|
|
|
|
return;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
fileStream = fileDao.GetFileStream(file); // getStream to fix file.ContentLength
|
|
|
|
|
|
|
|
long offset = 0;
|
|
|
|
var length = file.ContentLength;
|
|
|
|
if (fileStream.CanSeek)
|
|
|
|
{
|
|
|
|
length = ProcessRangeHeader(context, file.ContentLength, ref offset);
|
|
|
|
fileStream.Seek(offset, SeekOrigin.Begin);
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
flushed = await SendStreamByChunksAsync(context, length, title, fileStream, flushed);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
}
|
|
|
|
catch (ThreadAbortException tae)
|
|
|
|
{
|
|
|
|
Logger.Error("DownloadFile", tae);
|
|
|
|
}
|
|
|
|
catch (HttpException e)
|
|
|
|
{
|
|
|
|
Logger.Error("DownloadFile", e);
|
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
if (fileStream != null)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
fileStream.Close();
|
2020-04-13 11:46:26 +00:00
|
|
|
await fileStream.DisposeAsync();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
|
|
|
flushed = true;
|
|
|
|
}
|
|
|
|
catch (HttpException ex)
|
|
|
|
{
|
|
|
|
Logger.Error("DownloadFile", ex);
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ThreadAbortException tae)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DownloadFile", tae);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
// Get stack trace for the exception with source file information
|
|
|
|
var st = new StackTrace(ex, true);
|
|
|
|
// Get the top stack frame
|
|
|
|
var frame = st.GetFrame(0);
|
|
|
|
// Get the line number from the stack frame
|
|
|
|
var line = frame.GetFileLineNumber();
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("Url: {0} {1} IsClientConnected:{2}, line number:{3} frame:{4}", context.Request.Url(), ex, !context.RequestAborted.IsCancellationRequested, line, frame);
|
|
|
|
if (!flushed && !context.RequestAborted.IsCancellationRequested)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
context.Response.StatusCode = 400;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(HttpUtility.HtmlEncode(ex.Message));
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
private long ProcessRangeHeader(HttpContext context, long fullLength, ref long offset)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
if (context == null) throw new ArgumentNullException();
|
2020-02-05 13:33:09 +00:00
|
|
|
if (context.Request.Headers["Range"].FirstOrDefault() == null) return fullLength;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
long endOffset = -1;
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var range = context.Request.Headers["Range"].FirstOrDefault().Split(new[] { '=', '-' });
|
2020-01-27 11:15:18 +00:00
|
|
|
offset = Convert.ToInt64(range[1]);
|
|
|
|
if (range.Count() > 2 && !string.IsNullOrEmpty(range[2]))
|
|
|
|
{
|
|
|
|
endOffset = Convert.ToInt64(range[2]);
|
|
|
|
}
|
|
|
|
if (endOffset < 0 || endOffset >= fullLength)
|
|
|
|
{
|
|
|
|
endOffset = fullLength - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
var length = endOffset - offset + 1;
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (length <= 0) throw new HttpException(HttpStatusCode.RequestedRangeNotSatisfiable);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.InfoFormat("Starting file download (chunk {0}-{1})", offset, endOffset);
|
2020-01-27 11:15:18 +00:00
|
|
|
if (length < fullLength)
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.PartialContent;
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Accept-Ranges", "bytes");
|
|
|
|
context.Response.Headers.Add("Content-Range", string.Format(" bytes {0}-{1}/{2}", offset, endOffset, fullLength));
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task<bool> SendStreamByChunksAsync(HttpContext context, long toRead, string title, Stream fileStream, bool flushed)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.Buffer = false;
|
|
|
|
context.Response.Headers.Add("Connection", "Keep-Alive");
|
|
|
|
context.Response.Headers.Add("Content-Length", toRead.ToString(CultureInfo.InvariantCulture));
|
|
|
|
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(title));
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.ContentType = MimeMapping.GetMimeMapping(title);
|
|
|
|
|
|
|
|
const int bufferSize = 32 * 1024; // 32KB
|
|
|
|
var buffer = new byte[bufferSize];
|
|
|
|
while (toRead > 0)
|
|
|
|
{
|
|
|
|
var length = fileStream.Read(buffer, 0, bufferSize);
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!context.RequestAborted.IsCancellationRequested)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.WriteAsync(buffer, 0, length);
|
|
|
|
await context.Response.Body.FlushAsync();
|
2020-01-27 11:15:18 +00:00
|
|
|
flushed = true;
|
|
|
|
toRead -= length;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
toRead = -1;
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Warn(string.Format("IsClientConnected is false. Why? Download file {0} Connection is lost. ", title));
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-13 11:46:26 +00:00
|
|
|
|
|
|
|
return flushed;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task StreamFile(HttpContext context)
|
2020-03-03 11:37:40 +00:00
|
|
|
{
|
|
|
|
var q = context.Request.Query[FilesLinkUtility.FileId];
|
|
|
|
|
|
|
|
if (int.TryParse(q, out var id))
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await StreamFile(context, id);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await StreamFile(context, q);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task StreamFile<T>(HttpContext context, T id)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-03-03 11:37:40 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!int.TryParse(context.Request.Query[FilesLinkUtility.Version].FirstOrDefault() ?? "", out var version))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
version = 0;
|
|
|
|
}
|
|
|
|
var doc = context.Request.Query[FilesLinkUtility.DocShareKey];
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
fileDao.InvalidateCache(id);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var linkRight = FileShareLink.Check(doc, fileDao, out var file);
|
|
|
|
if (linkRight == FileShare.Restrict && !SecurityContext.IsAuthenticated)
|
|
|
|
{
|
|
|
|
var auth = context.Request.Query[FilesLinkUtility.AuthKey];
|
2020-03-03 11:37:40 +00:00
|
|
|
var validateResult = EmailValidationKeyProvider.ValidateEmailKey(id.ToString() + version, auth.FirstOrDefault() ?? "", Global.StreamUrlExpire);
|
2020-02-05 13:33:09 +00:00
|
|
|
if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!string.IsNullOrEmpty(FileUtility.SignatureSecret))
|
|
|
|
{
|
|
|
|
try
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault();
|
|
|
|
if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer "))
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
throw new Exception("Invalid header " + header);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-04-16 13:07:30 +00:00
|
|
|
header = header.Substring("Bearer ".Length);
|
2020-02-05 13:33:09 +00:00
|
|
|
|
|
|
|
var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret);
|
|
|
|
|
|
|
|
Logger.Debug("DocService StreamFile payload: " + stringPayload);
|
|
|
|
//var data = JObject.Parse(stringPayload);
|
|
|
|
//if (data == null)
|
|
|
|
//{
|
|
|
|
// throw new ArgumentException("DocService StreamFile header is incorrect");
|
|
|
|
//}
|
|
|
|
|
|
|
|
//var signedStringUrl = data["url"] ?? (data["payload"] != null ? data["payload"]["url"] : null);
|
|
|
|
//if (signedStringUrl == null)
|
|
|
|
//{
|
|
|
|
// throw new ArgumentException("DocService StreamFile header url is incorrect");
|
|
|
|
//}
|
|
|
|
//var signedUrl = new Uri(signedStringUrl.ToString());
|
|
|
|
|
|
|
|
//var signedQuery = signedUrl.Query;
|
|
|
|
//if (!context.Request.Url.Query.Equals(signedQuery))
|
|
|
|
//{
|
|
|
|
// throw new SecurityException(string.Format("DocService StreamFile header id not equals: {0} and {1}", context.Request.Url.Query, signedQuery));
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Logger.Error("Download stream header " + context.Request.Url(), ex);
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file == null
|
|
|
|
|| version > 0 && file.Version != version)
|
|
|
|
{
|
|
|
|
file = version > 0
|
|
|
|
? fileDao.GetFile(id, version)
|
|
|
|
: fileDao.GetFile(id);
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file == null)
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !FileSecurity.CanRead(file))
|
2020-02-05 13:33:09 +00:00
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!string.IsNullOrEmpty(file.Error))
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(file.Error);
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(file.Title));
|
|
|
|
context.Response.ContentType = MimeMapping.GetMimeMapping(file.Title);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-04-23 11:50:34 +00:00
|
|
|
using var stream = fileDao.GetFileStream(file);
|
|
|
|
context.Response.Headers.Add("Content-Length",
|
|
|
|
stream.CanSeek
|
|
|
|
? stream.Length.ToString(CultureInfo.InvariantCulture)
|
|
|
|
: file.ContentLength.ToString(CultureInfo.InvariantCulture));
|
|
|
|
await stream.CopyToAsync(context.Response.Body, StreamExtension.BufferSize);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("Error for: " + context.Request.Url(), ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(ex.Message);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (HttpException he)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("StreamFile", he);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task EmptyFile(HttpContext context)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var fileName = context.Request.Query[FilesLinkUtility.FileTitle];
|
2020-01-27 11:15:18 +00:00
|
|
|
if (!string.IsNullOrEmpty(FileUtility.SignatureSecret))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault();
|
2020-01-27 11:15:18 +00:00
|
|
|
if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer "))
|
|
|
|
{
|
|
|
|
throw new Exception("Invalid header " + header);
|
|
|
|
}
|
|
|
|
|
2020-04-16 13:07:30 +00:00
|
|
|
header = header.Substring("Bearer ".Length);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret);
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Debug("DocService EmptyFile payload: " + stringPayload);
|
2020-01-27 11:15:18 +00:00
|
|
|
//var data = JObject.Parse(stringPayload);
|
|
|
|
//if (data == null)
|
|
|
|
//{
|
|
|
|
// throw new ArgumentException("DocService EmptyFile header is incorrect");
|
|
|
|
//}
|
|
|
|
|
|
|
|
//var signedStringUrl = data["url"] ?? (data["payload"] != null ? data["payload"]["url"] : null);
|
|
|
|
//if (signedStringUrl == null)
|
|
|
|
//{
|
|
|
|
// throw new ArgumentException("DocService EmptyFile header url is incorrect");
|
|
|
|
//}
|
|
|
|
//var signedUrl = new Uri(signedStringUrl.ToString());
|
|
|
|
|
|
|
|
//var signedQuery = signedUrl.Query;
|
|
|
|
//if (!context.Request.Url.Query.Equals(signedQuery))
|
|
|
|
//{
|
|
|
|
// throw new SecurityException(string.Format("DocService EmptyFile header id not equals: {0} and {1}", context.Request.Url.Query, signedQuery));
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("Download stream header " + context.Request.Url(), ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var toExtension = FileUtility.GetFileExtension(fileName);
|
|
|
|
var fileExtension = FileUtility.GetInternalExtension(toExtension);
|
|
|
|
fileName = "new" + fileExtension;
|
|
|
|
var path = FileConstant.NewDocPath
|
2020-02-05 13:33:09 +00:00
|
|
|
+ (CoreBaseSettings.CustomMode ? "ru-RU/" : "default/")
|
2020-01-27 11:15:18 +00:00
|
|
|
+ fileName;
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var storeTemplate = GlobalStore.GetStoreTemplate();
|
2020-01-27 11:15:18 +00:00
|
|
|
if (!storeTemplate.IsFile("", path))
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(fileName));
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.ContentType = MimeMapping.GetMimeMapping(fileName);
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
using var stream = storeTemplate.GetReadStream("", path);
|
|
|
|
context.Response.Headers.Add("Content-Length",
|
|
|
|
stream.CanSeek
|
|
|
|
? stream.Length.ToString(CultureInfo.InvariantCulture)
|
|
|
|
: storeTemplate.GetFileSize("", path).ToString(CultureInfo.InvariantCulture));
|
|
|
|
stream.StreamCopyTo(context.Response.Body);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("Error for: " + context.Request.Url(), ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(ex.Message);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (HttpException he)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("EmptyFile", he);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task TempFile(HttpContext context)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var fileName = context.Request.Query[FilesLinkUtility.FileTitle];
|
|
|
|
var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileName, auth ?? "", Global.StreamUrlExpire);
|
|
|
|
if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok)
|
|
|
|
{
|
|
|
|
var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException);
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.Response.Clear();
|
|
|
|
context.Response.ContentType = MimeMapping.GetMimeMapping(fileName);
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(fileName));
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var store = GlobalStore.GetStore();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var path = Path.Combine("temp_stream", fileName);
|
|
|
|
|
|
|
|
if (!store.IsFile(FileConstant.StorageDomainTmp, path))
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_FileNotFound);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
using (var readStream = store.GetReadStream(FileConstant.StorageDomainTmp, path))
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Content-Length", readStream.Length.ToString(CultureInfo.InvariantCulture));
|
2020-04-23 11:50:34 +00:00
|
|
|
await readStream.CopyToAsync(context.Response.Body, StreamExtension.BufferSize);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
store.Delete(FileConstant.StorageDomainTmp, path);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (HttpException he)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("TempFile", he);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task DifferenceFile(HttpContext context)
|
2020-03-03 11:37:40 +00:00
|
|
|
{
|
|
|
|
var q = context.Request.Query[FilesLinkUtility.FileId];
|
|
|
|
|
|
|
|
if (int.TryParse(q, out var id))
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await DifferenceFile(context, id);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await DifferenceFile(context, q);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task DifferenceFile<T>(HttpContext context, T id)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-03-03 11:37:40 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
int.TryParse(context.Request.Query[FilesLinkUtility.Version].FirstOrDefault() ?? "", out var version);
|
|
|
|
var doc = context.Request.Query[FilesLinkUtility.DocShareKey];
|
|
|
|
|
|
|
|
var linkRight = FileShareLink.Check(doc, fileDao, out var file);
|
|
|
|
if (linkRight == FileShare.Restrict && !SecurityContext.IsAuthenticated)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault();
|
2020-03-03 11:37:40 +00:00
|
|
|
var validateResult = EmailValidationKeyProvider.ValidateEmailKey(id.ToString() + version, auth ?? "", Global.StreamUrlExpire);
|
2020-02-05 13:33:09 +00:00
|
|
|
if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var exc = new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error($"{FilesLinkUtility.AuthKey} {validateResult}: {context.Request.Url()}", exc);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(FilesCommonResource.ErrorMassage_SecurityException);
|
2020-02-05 13:33:09 +00:00
|
|
|
return;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
fileDao.InvalidateCache(id);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file == null
|
|
|
|
|| version > 0 && file.Version != version)
|
|
|
|
{
|
|
|
|
file = version > 0
|
|
|
|
? fileDao.GetFile(id, version)
|
|
|
|
: fileDao.GetFile(id);
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (file == null)
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !FileSecurity.CanRead(file))
|
2020-02-05 13:33:09 +00:00
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
if (!string.IsNullOrEmpty(file.Error))
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(file.Error);
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
|
|
|
return;
|
|
|
|
}
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(".zip"));
|
|
|
|
context.Response.ContentType = MimeMapping.GetMimeMapping(".zip");
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
using (var stream = fileDao.GetDifferenceStream(file))
|
|
|
|
{
|
|
|
|
context.Response.Headers.Add("Content-Length", stream.Length.ToString(CultureInfo.InvariantCulture));
|
2020-04-23 11:50:34 +00:00
|
|
|
await stream.CopyToAsync(context.Response.Body, StreamExtension.BufferSize);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(ex.Message);
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("Error for: " + context.Request.Url(), ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.Body.FlushAsync();
|
2020-02-05 13:33:09 +00:00
|
|
|
//context.Response.SuppressContent = true;
|
|
|
|
//context.ApplicationInstance.CompleteRequest();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (HttpException he)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("DifferenceFile", he);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
private static string GetEtag<T>(File<T> file)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
return file.ID + ":" + file.Version + ":" + file.Title.GetHashCode() + ":" + file.ContentLength;
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task CreateFile(HttpContext context)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var folderId = context.Request.Query[FilesLinkUtility.FolderId].FirstOrDefault();
|
2020-01-27 11:15:18 +00:00
|
|
|
if (string.IsNullOrEmpty(folderId))
|
2020-03-03 11:37:40 +00:00
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await CreateFile(context, GlobalFolderHelper.FolderMy);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await CreateFile(context, folderId);
|
2020-03-03 11:37:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task CreateFile<T>(HttpContext context, T folderId)
|
2020-03-03 11:37:40 +00:00
|
|
|
{
|
|
|
|
var responseMessage = context.Request.Query["response"] == "message";
|
|
|
|
Folder<T> folder;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
var folderDao = DaoFactory.GetFolderDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
folder = folderDao.GetFolder(folderId);
|
|
|
|
|
2020-01-27 11:15:18 +00:00
|
|
|
if (folder == null) throw new HttpException((int)HttpStatusCode.NotFound, FilesCommonResource.ErrorMassage_FolderNotFound);
|
2020-03-04 14:40:05 +00:00
|
|
|
if (!FileSecurity.CanCreate(folder)) throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException_Create);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
File<T> file;
|
2020-02-05 13:33:09 +00:00
|
|
|
var fileUri = context.Request.Query[FilesLinkUtility.FileUri];
|
|
|
|
var fileTitle = context.Request.Query[FilesLinkUtility.FileTitle];
|
2020-01-27 11:15:18 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(fileUri))
|
|
|
|
{
|
|
|
|
file = CreateFileFromUri(folder, fileUri, fileTitle);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var docType = context.Request.Query["doctype"];
|
2020-01-27 11:15:18 +00:00
|
|
|
file = CreateFileFromTemplate(folder, fileTitle, docType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error(ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
if (responseMessage)
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync("error: " + ex.Message);
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
context.Response.Redirect(PathProvider.StartURL + "#error/" + HttpUtility.UrlEncode(ex.Message), true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-05 07:24:50 +00:00
|
|
|
FileMarker.MarkAsNew(file);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
if (responseMessage)
|
|
|
|
{
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync("ok: " + string.Format(FilesCommonResource.MessageFileCreated, folder.Title));
|
2020-01-27 11:15:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
context.Response.Redirect(
|
2020-02-05 13:33:09 +00:00
|
|
|
(context.Request.Query["openfolder"].FirstOrDefault() ?? "").Equals("true")
|
2020-01-27 11:15:18 +00:00
|
|
|
? PathProvider.GetFolderUrl(file.FolderID)
|
|
|
|
: (FilesLinkUtility.GetFileWebEditorUrl(file.ID) + "#message/" + HttpUtility.UrlEncode(string.Format(FilesCommonResource.MessageFileCreated, folder.Title))));
|
|
|
|
}
|
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
private File<T> CreateFileFromTemplate<T>(Folder<T> folder, string fileTitle, string docType)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var storeTemplate = GlobalStore.GetStoreTemplate();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
var lang = UserManager.GetUsers(SecurityContext.CurrentAccount.ID).GetCulture();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var fileExt = FileUtility.InternalExtension[FileType.Document];
|
|
|
|
if (!string.IsNullOrEmpty(docType))
|
|
|
|
{
|
2020-03-03 11:37:40 +00:00
|
|
|
var tmpFileType = Configuration<T>.DocType.FirstOrDefault(r => r.Value.Equals(docType, StringComparison.OrdinalIgnoreCase));
|
2020-02-05 13:33:09 +00:00
|
|
|
FileUtility.InternalExtension.TryGetValue(tmpFileType.Key, out var tmpFileExt);
|
2020-01-27 11:15:18 +00:00
|
|
|
if (!string.IsNullOrEmpty(tmpFileExt))
|
|
|
|
fileExt = tmpFileExt;
|
|
|
|
}
|
|
|
|
|
|
|
|
var templateName = "new" + fileExt;
|
|
|
|
|
|
|
|
var templatePath = FileConstant.NewDocPath + lang + "/";
|
|
|
|
if (!storeTemplate.IsDirectory(templatePath))
|
|
|
|
templatePath = FileConstant.NewDocPath + "default/";
|
|
|
|
templatePath += templateName;
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(fileTitle))
|
|
|
|
{
|
|
|
|
fileTitle = templateName;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-02-10 16:04:54 +00:00
|
|
|
fileTitle += fileExt;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
var file = ServiceProvider.GetService<File<T>>();
|
2020-02-10 16:04:54 +00:00
|
|
|
file.Title = fileTitle;
|
|
|
|
file.FolderID = folder.ID;
|
|
|
|
file.Comment = FilesCommonResource.CommentCreate;
|
2020-02-05 13:33:09 +00:00
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
var stream = storeTemplate.GetReadStream("", templatePath);
|
|
|
|
file.ContentLength = stream.CanSeek ? stream.Length : storeTemplate.GetFileSize(templatePath);
|
|
|
|
return fileDao.SaveFile(file, stream);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-03-04 14:40:05 +00:00
|
|
|
private File<T> CreateFileFromUri<T>(Folder<T> folder, string fileUri, string fileTitle)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(fileTitle))
|
|
|
|
fileTitle = Path.GetFileName(HttpUtility.UrlDecode(fileUri));
|
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
var file = ServiceProvider.GetService<File<T>>();
|
2020-02-10 16:04:54 +00:00
|
|
|
file.Title = fileTitle;
|
|
|
|
file.FolderID = folder.ID;
|
|
|
|
file.Comment = FilesCommonResource.CommentCreate;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var req = (HttpWebRequest)WebRequest.Create(fileUri);
|
|
|
|
|
|
|
|
// hack. http://ubuntuforums.org/showthread.php?t=1841740
|
|
|
|
if (WorkContext.IsMono)
|
|
|
|
{
|
|
|
|
ServicePointManager.ServerCertificateValidationCallback += (s, ce, ca, p) => true;
|
|
|
|
}
|
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
var fileStream = new ResponseStream(req.GetResponse());
|
|
|
|
file.ContentLength = fileStream.Length;
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
return fileDao.SaveFile(file, fileStream);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
private void Redirect(HttpContext context)
|
2020-03-03 11:37:40 +00:00
|
|
|
{
|
|
|
|
var q = context.Request.Query[FilesLinkUtility.FileId];
|
|
|
|
var q1 = context.Request.Query[FilesLinkUtility.FolderId];
|
|
|
|
|
|
|
|
if (int.TryParse(q, out var fileId) && int.TryParse(q1, out var folderId))
|
|
|
|
{
|
|
|
|
Redirect(context, fileId, folderId);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Redirect(context, q, q1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Redirect<T>(HttpContext context, T folderId, T fileId)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
if (!SecurityContext.AuthenticateMe(CookiesManager.GetCookies(CookiesType.AuthKey)))
|
|
|
|
{
|
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var urlRedirect = string.Empty;
|
2020-03-03 11:37:40 +00:00
|
|
|
if (folderId != null)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
urlRedirect = PathProvider.GetFolderUrl(folderId);
|
|
|
|
}
|
|
|
|
catch (ArgumentNullException e)
|
|
|
|
{
|
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 11:37:40 +00:00
|
|
|
if (fileId != null)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-03-03 11:37:40 +00:00
|
|
|
var fileDao = DaoFactory.GetFileDao<T>();
|
2020-02-05 13:33:09 +00:00
|
|
|
var file = fileDao.GetFile(fileId);
|
|
|
|
if (file == null)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
|
|
return;
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
|
|
|
|
urlRedirect = FilesLinkUtility.GetFileWebPreviewUrl(FileUtility, file.Title, file.ID);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(urlRedirect))
|
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, FilesCommonResource.ErrorMassage_BadRequest);
|
|
|
|
context.Response.Redirect(urlRedirect);
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
private async Task TrackFile(HttpContext context)
|
2020-01-27 11:15:18 +00:00
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
var auth = context.Request.Query[FilesLinkUtility.AuthKey].FirstOrDefault();
|
|
|
|
var fileId = context.Request.Query[FilesLinkUtility.FileId].FirstOrDefault();
|
|
|
|
Logger.Debug("DocService track fileid: " + fileId);
|
2020-01-27 11:15:18 +00:00
|
|
|
|
|
|
|
var callbackSpan = TimeSpan.FromDays(128);
|
|
|
|
var validateResult = EmailValidationKeyProvider.ValidateEmailKey(fileId, auth ?? "", callbackSpan);
|
|
|
|
if (validateResult != EmailValidationKeyProvider.ValidationResult.Ok)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.ErrorFormat("DocService track auth error: {0}, {1}: {2}", validateResult.ToString(), FilesLinkUtility.AuthKey, auth);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException);
|
|
|
|
}
|
|
|
|
|
|
|
|
DocumentServiceTracker.TrackerData fileData;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
string body;
|
2020-02-05 13:33:09 +00:00
|
|
|
var receiveStream = context.Request.Body;
|
|
|
|
var readStream = new StreamReader(receiveStream);
|
|
|
|
body = readStream.ReadToEnd();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Debug("DocService track body: " + body);
|
2020-01-27 11:15:18 +00:00
|
|
|
if (string.IsNullOrEmpty(body))
|
|
|
|
{
|
|
|
|
throw new ArgumentException("DocService request body is incorrect");
|
|
|
|
}
|
|
|
|
|
|
|
|
var data = JToken.Parse(body);
|
|
|
|
if (data == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentException("DocService request is incorrect");
|
|
|
|
}
|
|
|
|
fileData = data.ToObject<DocumentServiceTracker.TrackerData>();
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DocService track error read body", e);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(FileUtility.SignatureSecret))
|
|
|
|
{
|
|
|
|
if (!string.IsNullOrEmpty(fileData.Token))
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var dataString = JsonWebToken.Decode(fileData.Token, FileUtility.SignatureSecret);
|
|
|
|
var data = JObject.Parse(dataString);
|
|
|
|
if (data == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentException("DocService request token is incorrect");
|
|
|
|
}
|
|
|
|
fileData = data.ToObject<DocumentServiceTracker.TrackerData>();
|
|
|
|
}
|
|
|
|
catch (SignatureVerificationException ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DocService track header", ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.Forbidden, ex.Message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//todo: remove old scheme
|
2020-02-05 13:33:09 +00:00
|
|
|
var header = context.Request.Headers[FileUtility.SignatureHeader].FirstOrDefault();
|
2020-01-27 11:15:18 +00:00
|
|
|
if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer "))
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DocService track header is null");
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.Forbidden, FilesCommonResource.ErrorMassage_SecurityException);
|
|
|
|
}
|
|
|
|
header = header.Substring("Bearer ".Length);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret);
|
|
|
|
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Debug("DocService track payload: " + stringPayload);
|
2020-01-27 11:15:18 +00:00
|
|
|
var jsonPayload = JObject.Parse(stringPayload);
|
|
|
|
var data = jsonPayload["payload"];
|
|
|
|
if (data == null)
|
|
|
|
{
|
|
|
|
throw new ArgumentException("DocService request header is incorrect");
|
|
|
|
}
|
|
|
|
fileData = data.ToObject<DocumentServiceTracker.TrackerData>();
|
|
|
|
}
|
|
|
|
catch (SignatureVerificationException ex)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DocService track header", ex);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.Forbidden, ex.Message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DocumentServiceTracker.TrackResponse result;
|
|
|
|
try
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
result = DocumentServiceTrackerHelper.ProcessData(fileId, fileData);
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
2020-02-05 13:33:09 +00:00
|
|
|
Logger.Error("DocService track:", e);
|
2020-01-27 11:15:18 +00:00
|
|
|
throw new HttpException((int)HttpStatusCode.BadRequest, e.Message);
|
|
|
|
}
|
2020-02-05 13:33:09 +00:00
|
|
|
result ??= new DocumentServiceTracker.TrackResponse();
|
2020-01-27 11:15:18 +00:00
|
|
|
|
2020-04-13 11:46:26 +00:00
|
|
|
await context.Response.WriteAsync(DocumentServiceTracker.TrackResponse.Serialize(result));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class FileHandlerExtensions
|
|
|
|
{
|
|
|
|
public static DIHelper AddFileHandlerService(this DIHelper services)
|
|
|
|
{
|
|
|
|
services.TryAddScoped<FileHandlerService>();
|
|
|
|
return services
|
|
|
|
.AddFilesLinkUtilityService()
|
|
|
|
.AddTenantExtraService()
|
|
|
|
.AddCookiesManagerService()
|
|
|
|
.AddAuthContextService()
|
|
|
|
.AddSecurityContextService()
|
|
|
|
.AddGlobalStoreService()
|
|
|
|
.AddDaoFactoryService()
|
|
|
|
.AddFileSecurityService()
|
|
|
|
.AddFileMarkerService()
|
|
|
|
.AddSetupInfo()
|
|
|
|
.AddFileUtilityService()
|
|
|
|
.AddGlobalService()
|
|
|
|
.AddEmailValidationKeyProviderService()
|
|
|
|
.AddCoreBaseSettingsService()
|
|
|
|
.AddGlobalFolderHelperService()
|
|
|
|
.AddPathProviderService()
|
|
|
|
.AddUserManagerService()
|
|
|
|
.AddDocumentServiceTrackerHelperService()
|
|
|
|
.AddFilesMessageService()
|
|
|
|
.AddFileConverterService()
|
|
|
|
.AddFileShareLinkService()
|
|
|
|
.AddFFmpegServiceService();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static IApplicationBuilder UseFileHandler(this IApplicationBuilder builder)
|
|
|
|
{
|
|
|
|
return builder.UseMiddleware<FileHandler>();
|
2020-01-27 11:15:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|