/// <summary> /// Move file in another space on the server. /// </summary> /// <param name="file">File info to move.</param> /// <param name="destinationPath">Destination path on the server.</param> /// <returns>True or false operation result.</returns> public async Task <bool> MoveAsync(File file, string destinationPath) { var link = await _linkManager.GetItemLink(file.FullPath, false); if (link != null) { var remapped = await _linkManager.RemapLink(link, destinationPath); if (remapped) { _itemCache.Invalidate(file.Path, destinationPath); } return(remapped); } var qry = file.Files .AsParallel() .WithDegreeOfParallelism(Math.Min(MaxInnerParallelRequests, file.Files.Count)) .Select(async pfile => { var moveres = await Account.RequestRepo.Move(pfile.FullPath, destinationPath); _itemCache.Forget(file.Path, pfile.FullPath); return(moveres); }); _itemCache.Forget(file.Path, file.FullPath); _itemCache.Invalidate(destinationPath); bool res = (await Task.WhenAll(qry)) .All(r => r.IsSuccess); return(res); }
public async Task <Stream> Create(File file, FileUploadedDelegate onUploaded = null, bool discardEncryption = false) { if (!(await _cloud.GetItemAsync(file.Path, Cloud.ItemType.Folder) is Folder folder)) { throw new DirectoryNotFoundException(file.Path); } Stream stream; bool cryptRequired = _cloud.IsFileExists(CryptFileInfo.FileName, WebDavPath.GetParents(folder.FullPath).ToList()).Any(); if (cryptRequired && !discardEncryption) { if (!_cloud.Account.Credentials.CanCrypt) { throw new Exception($"Cannot upload {file.FullPath} to crypt folder without additional password!"); } // #142 remove crypted file parts if size changed var remoteFile = folder.Files.FirstOrDefault(f => f.FullPath == file.FullPath); if (remoteFile != null) { await _cloud.Remove(remoteFile); } stream = GetCryptoStream(file, onUploaded); } else { stream = GetPlainStream(file, onUploaded); } return(stream); }
private Stream GetCryptoStream(File file, FileUploadedDelegate onUploaded) { var info = CryptoUtil.GetCryptoKeyAndSalt(_cloud.Account.Credentials.PasswordCrypt); var xts = XtsAes256.Create(info.Key, info.IV); file.ServiceInfo.CryptInfo = new CryptInfo { PublicKey = new CryptoKeyInfo { Salt = info.Salt, IV = info.IV }, AlignBytes = (uint)(file.Size % XTSWriteOnlyStream.BlockSize != 0 ? XTSWriteOnlyStream.BlockSize - file.Size % XTSWriteOnlyStream.BlockSize : 0) }; var size = file.OriginalSize % XTSWriteOnlyStream.BlockSize == 0 ? file.OriginalSize.DefaultValue : (file.OriginalSize / XTSWriteOnlyStream.BlockSize + 1) * XTSWriteOnlyStream.BlockSize; var ustream = new SplittedUploadStream(file.FullPath, _cloud, size, false, file.ServiceInfo.CryptInfo); if (onUploaded != null) { ustream.FileUploaded += onUploaded; } // ReSharper disable once RedundantArgumentDefaultValue var encustream = new XTSWriteOnlyStream(ustream, xts, XTSWriteOnlyStream.DefaultSectorSize); return(encustream); }
private Stream CreateXTSStream(File file, long?start = null, long?end = null) { var pub = CryptoUtil.GetCryptoPublicInfo(_cloud, file); var key = CryptoUtil.GetCryptoKey(_cloud.Account.Credentials.PasswordCrypt, pub.Salt); var xts = XtsAes256.Create(key, pub.IV); long fileLength = file.OriginalSize; long requestedOffset = start ?? 0; long requestedEnd = end ?? fileLength; long alignedOffset = requestedOffset / XTSSectorSize * XTSSectorSize; long alignedEnd = requestedEnd % XTSBlockSize == 0 ? requestedEnd : (requestedEnd / XTSBlockSize + 1) * XTSBlockSize; if (alignedEnd == 0) { alignedEnd = 16; } var downStream = _cloud.Account.RequestRepo.GetDownloadStream(file, alignedOffset, alignedEnd); ulong startSector = (ulong)alignedOffset / XTSSectorSize; int trimStart = (int)(requestedOffset - alignedOffset); uint trimEnd = alignedEnd == fileLength ? file.ServiceInfo.CryptInfo.AlignBytes : (uint)(alignedEnd - requestedEnd); var xtsStream = new XTSReadOnlyStream(downStream, xts, XTSSectorSize, startSector, trimStart, trimEnd); return(xtsStream); }
public LocalStoreItem(File fileInfo, bool isWritable, LocalStore store) { _store = store; _fileInfo = fileInfo; IsWritable = isWritable; }
public async Task Unpublish(File file) { foreach (var innerFile in file.Files) { await Unpublish(innerFile.GetPublicLinks(this).First().Uri, innerFile.FullPath); innerFile.PublicLinks.Clear(); } _itemCache.Invalidate(file.FullPath, file.Path); }
public async Task <PublishInfo> Publish(IEntry entry, bool makeShareFile = true, bool generateDirectVideoLink = false, bool makeM3UFile = false, SharedVideoResolution videoResolution = SharedVideoResolution.All) { return(entry switch { null => throw new ArgumentNullException(nameof(entry)), File file => await Publish(file, makeShareFile, generateDirectVideoLink, makeM3UFile, videoResolution), Folder folder => await Publish(folder, makeShareFile), _ => throw new Exception($"Unknow entry type, type = {entry.GetType()},path = {entry.FullPath}") });
public async Task Unpublish(File file) { foreach (var innerFile in file.Files) { await Unpublish(innerFile.GetPublicLink(this)); innerFile.PublicLink = string.Empty; } _itemCache.Invalidate(file.FullPath, file.Path); }
private Stream GetPlainStream(File file, FileUploadedDelegate onUploaded) { var stream = new SplittedUploadStream(file.FullPath, _cloud, file.Size); if (onUploaded != null) { stream.FileUploaded += onUploaded; } return(stream); }
public Stream Create(File file, long?start = null, long?end = null) { if (file.ServiceInfo.IsCrypted) { return(CreateXTSStream(file, start, end)); } //return new DownloadStream(file, _cloud.CloudApi, start, end); var stream = _cloud.Account.RequestRepo.GetDownloadStream(file, start, end); return(stream); }
/// <summary> /// Remove the file on server. /// </summary> /// <param name="file">File info.</param> /// <param name="removeShareDescription">Also remove share description file (.share.wdmrc)</param> /// <param name="doInvalidateCache"></param> /// <returns>True or false operation result.</returns> public virtual async Task <bool> Remove(File file, bool removeShareDescription = true, bool doInvalidateCache = true) { // remove all parts if file splitted var qry = file.Files .AsParallel() .WithDegreeOfParallelism(Math.Min(MaxInnerParallelRequests, file.Files.Count)) .Select(async pfile => { var removed = await Remove(pfile.FullPath); return(removed); }); bool res = (await Task.WhenAll(qry)).All(r => r); if (res) { //unshare master item if (file.Name.EndsWith(PublishInfo.SharedFilePostfix)) { var mpath = WebDavPath.Clean(file.FullPath.Substring(0, file.FullPath.Length - PublishInfo.SharedFilePostfix.Length)); var item = await GetItemAsync(mpath); if (item is Folder folder) { await Unpublish(folder.GetPublicLink(this)); } else if (item is File ifile) { await Unpublish(ifile); } } else { //remove share description (.wdmrc.share) if (removeShareDescription) { if (await GetItemAsync(file.FullPath + PublishInfo.SharedFilePostfix) is File sharefile) { await Remove(sharefile, false); } } } } if (doInvalidateCache) { _itemCache.Invalidate(file.Path, file.FullPath); } return(res); }
public Task <StoreItemResult> CreateItemAsync(string name, bool overwrite, IHttpContext httpContext) { if (!IsWritable) { return(Task.FromResult(new StoreItemResult(DavStatusCode.PreconditionFailed))); } var destinationPath = FullPath + "/" + name; var size = httpContext.Request.ContentLength(); var f = new File(destinationPath, size); return(Task.FromResult(new StoreItemResult(DavStatusCode.Created, new LocalStoreItem(f, IsWritable, _store)))); }
/// <summary> /// Rename file on the server. /// </summary> /// <param name="file">Source file info.</param> /// <param name="newFileName">New file name.</param> /// <returns>True or false operation result.</returns> public async Task <bool> Rename(File file, string newFileName) { var result = await Rename(file.FullPath, newFileName).ConfigureAwait(false); if (file.Files.Count > 1) { foreach (var splitFile in file.Parts) { string newSplitName = newFileName + splitFile.ServiceInfo.ToString(false); await Rename(splitFile.FullPath, newSplitName).ConfigureAwait(false); } } return(result); }
public Task <StoreItemResult> CreateItemAsync(string name, bool overwrite, IHttpContext httpContext) { if (!IsWritable) { return(Task.FromResult(new StoreItemResult(DavStatusCode.PreconditionFailed))); } var destinationPath = FullPath + "/" + name; DavStatusCode result = DavStatusCode.Created; var size = httpContext.Request.ContentLength(); var f = new File(destinationPath, size, null); return(Task.FromResult(new StoreItemResult(result, new MailruStoreItem(LockingManager, f, IsWritable)))); }
private void FillWithULinks(Folder folder) { if (!folder.IsChildsLoaded) { return; } var flinks = _linkManager.GetItems(folder.FullPath); if (flinks.Any()) { foreach (var flink in flinks) { string linkpath = WebDavPath.Combine(folder.FullPath, flink.Name); if (!flink.IsFile) { folder.Folders.Add(new Folder(0, linkpath) { CreationTimeUtc = flink.CreationDate ?? DateTime.MinValue }); } else { if (folder.Files.All(inf => inf.FullPath != linkpath)) { var newfile = new File(linkpath, flink.Size); { newfile.PublicLinks.Add(new PublicLinkInfo(flink.Href)); } if (flink.CreationDate != null) { newfile.LastWriteTimeUtc = flink.CreationDate.Value; } folder.Files.Add(newfile); } } } } foreach (var childFolder in folder.Folders) { FillWithULinks(childFolder); } }
private void Initialize() { long allowedSize = _maxFileSize; //TODO: make it right //- BytesCount(_file.Name); _performAsSplitted = _size > allowedSize || _cryptInfo != null; _origfile = new File(_destinationPath, _size, null); if (!_performAsSplitted) // crypted are performed alike splitted file { _files.Add(_origfile); } else { var sinfo = new FilenameServiceInfo { CleanName = _origfile.Name, CryptInfo = _cryptInfo, SplitInfo = new FileSplitInfo { IsHeader = false } }; int nfiles = (int)(_size / allowedSize + 1); if (nfiles > 999) { throw new OverflowException("Cannot upload more than 999 file parts"); } //TODO: move file splitting in File class for (int i = 1; i <= nfiles; i++) { sinfo.SplitInfo.PartNumber = i; sinfo.CryptInfo = i != nfiles ? null : _cryptInfo; var f = new File($"{_origfile.FullPath}{sinfo}", i != nfiles ? allowedSize : _size % allowedSize, null); _files.Add(f); } } NextFile(); }
public async Task <PublishInfo> Publish(File file, bool makeShareFile = true, bool generateDirectVideoLink = false, bool makeM3UFile = false, SharedVideoResolution videoResolution = SharedVideoResolution.All) { if (file.Files.Count > 1 && (generateDirectVideoLink || makeM3UFile)) { throw new ArgumentException($"Cannot generate direct video link for splitted file {file.FullPath}"); } foreach (var innerFile in file.Files) { var url = await Publish(innerFile.FullPath); innerFile.PublicLinks.Clear(); innerFile.PublicLinks.Add(new PublicLinkInfo(url)); } var info = file.ToPublishInfo(this, generateDirectVideoLink, videoResolution); if (makeShareFile) { string path = $"{file.FullPath}{PublishInfo.SharedFilePostfix}"; UploadFileJson(path, info) .ThrowIf(r => !r, r => new Exception($"Cannot upload JSON file, path = {path}")); } if (makeM3UFile) { string path = $"{file.FullPath}{PublishInfo.PlaylistFilePostfix}"; var content = new StringBuilder(); { content.Append("#EXTM3U\r\n"); foreach (var item in info.Items) { content.Append($"#EXTINF:-1,{WebDavPath.Name(item.Path)}\r\n"); content.Append($"{item.PlaylistUrl}\r\n"); } } UploadFile(path, content.ToString()) .ThrowIf(r => !r, r => new Exception($"Cannot upload JSON file, path = {path}")); } return(info); }
public LocalStoreItem(ILockingManager lockingManager, File fileInfo, bool isWritable) { LockingManager = lockingManager; _fileInfo = fileInfo; IsWritable = isWritable; }
/// <summary> /// Copy file to another path. /// </summary> /// <param name="file">Source file info.</param> /// <param name="destinationPath">Destination path.</param> /// <param name="newname">Rename target file.</param> /// <returns>True or false operation result.</returns> public async Task <bool> Copy(File file, string destinationPath, string newname) { string destPath = destinationPath; newname = string.IsNullOrEmpty(newname) ? file.Name : newname; bool doRename = file.Name != newname; var link = await _linkManager.GetItemLink(file.FullPath, false); // копируем не саму ссылку, а её содержимое if (link != null) { var cloneRes = await CloneItem(destPath, link.Href.OriginalString); if (doRename || WebDavPath.Name(cloneRes.Path) != newname) { string newFullPath = WebDavPath.Combine(destPath, WebDavPath.Name(cloneRes.Path)); var renameRes = await Rename(newFullPath, link.Name); if (!renameRes) { return(false); } } if (cloneRes.IsSuccess) { _itemCache.Invalidate(destinationPath); } return(cloneRes.IsSuccess); } var qry = file.Files .AsParallel() .WithDegreeOfParallelism(Math.Min(MaxInnerParallelRequests, file.Files.Count)) .Select(async pfile => { //var copyRes = await new CopyRequest(CloudApi, pfile.FullPath, destPath, ConflictResolver.Rewrite).MakeRequestAsync(); var copyRes = await Account.RequestRepo.Copy(pfile.FullPath, destPath, ConflictResolver.Rewrite); if (!copyRes.IsSuccess) { return(false); } if (doRename || WebDavPath.Name(copyRes.NewName) != newname) { string newFullPath = WebDavPath.Combine(destPath, WebDavPath.Name(copyRes.NewName)); var renameRes = await Rename(newFullPath, pfile.Name.Replace(file.Name, newname)); if (!renameRes) { return(false); } } return(true); }); _itemCache.Invalidate(destinationPath); bool res = (await Task.WhenAll(qry)) .All(r => r); return(res); }