DocSpace-client/products/ASC.Files/Server/HttpHandlers/FileHandler.ashx.cs

1290 lines
55 KiB
C#
Raw Normal View History

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();
await context.Response.CompleteAsync();
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;
await context.Response.CompleteAsync();
2020-02-05 13:33:09 +00:00
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
{
var cl = 0;
2020-02-05 13:33:09 +00:00
//context.Response.Buffer = false;
context.Response.Headers.Add("Connection", "Keep-Alive");
context.Response.Headers.Add("Content-Disposition", ContentDispositionUtil.GetHeaderValue(title));
2020-01-27 11:15:18 +00:00
context.Response.ContentType = MimeMapping.GetMimeMapping(title);
var bufferSize = Convert.ToInt32(Math.Min(Convert.ToInt64(32 * 1024), toRead)); // 32KB
2020-01-27 11:15:18 +00:00
var buffer = new byte[bufferSize];
while (toRead > 0)
{
var length = await fileStream.ReadAsync(buffer, 0, bufferSize);
cl += length;
await context.Response.Body.WriteAsync(buffer, 0, length, context.RequestAborted);
await context.Response.Body.FlushAsync();
flushed = true;
toRead -= length;
2020-01-27 11:15:18 +00:00
}
2020-04-13 11:46:26 +00:00
context.Response.Headers.Add("Content-Length", cl.ToString(CultureInfo.InvariantCulture));
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));
2020-05-06 08:41:27 +00:00
await stream.CopyToAsync(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("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));
2020-05-06 08:41:27 +00:00
await stream.CopyToAsync(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-05-06 08:41:27 +00:00
await readStream.CopyToAsync(context.Response.Body);
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-05-06 08:41:27 +00:00
await stream.CopyToAsync(context.Response.Body);
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
}
}
}