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 async Task <Stream> Create(File file, FileUploadedDelegate onUploaded = null, bool discardEncryption = false) { if (!(await _cloud.GetItemAsync(file.Path, MailRuCloud.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); }
/// <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> Move(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 => await MoveOrCopy(pfile.FullPath, destinationPath, true)); bool res = (await Task.WhenAll(qry)) .All(r => !string.IsNullOrEmpty(r)); return(res); }
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 GetPlainStream(File file, FileUploadedDelegate onUploaded) { var stream = new SplittedUploadStream(file.FullPath, _cloud, file.Size); if (onUploaded != null) { stream.FileUploaded += onUploaded; } return(stream); }
public async Task Unpublish(File file) { foreach (var innerFile in file.Files) { await Unpublish(innerFile.PublicLink); innerFile.PublicLink = string.Empty; } _itemCache.Invalidate(file.FullPath, file.Path); }
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); }
/// <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); }
/// <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); if (file.Parts.Count > 1) { foreach (var splitFile in file.Parts) { string newSplitName = newFileName + ".wdmrc" + splitFile.Extension; //TODO: refact with .wdmrc await Rename(splitFile.FullPath, newSplitName); } } return(result); }
/// <summary> /// Remove the file on server. /// </summary> /// <param name="file">File info.</param> /// <returns>True or false operation result.</returns> public virtual async Task <bool> Remove(File file) { 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); _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; 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) { PublicLink = flink.Href, }; if (flink.CreationDate != null) { newfile.LastWriteTimeUtc = flink.CreationDate.Value; } folder.Files.Add(newfile); } } } } foreach (var childFolder in folder.Folders) { FillWithULinks(childFolder); } }
public async Task <PublishInfo> Publish(File file, bool makeShareFile = true) { foreach (var innerFile in file.Files) { var url = await Publish(innerFile.FullPath); innerFile.PublicLink = url; } var info = file.ToPublishInfo(); if (makeShareFile) { string path = $"{file.FullPath}{PublishInfo.SharedFilePostfix}"; UploadFileJson(path, info) .ThrowIf(r => !r, r => new Exception($"Cannot upload JSON file, path = {path}")); } return(info); }
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.PublicLink = 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); }
///// <summary> ///// Get list of files and folders from account. ///// </summary> ///// <param name="path">Path in the cloud to return the list of the items.</param> ///// <param name="itemType">Unknown, File/Folder if you know for sure</param> ///// <param name="resolveLinks">True if you know for sure that's not a linked item</param> ///// <returns>List of the items.</returns> public virtual async Task <IEntry> GetItemAsync(string path, ItemType itemType = ItemType.Unknown, bool resolveLinks = true) { path = WebDavPath.Clean(path); var cached = _itemCache.Get(path); if (null != cached) { return(cached); } //TODO: subject to refact!!! var ulink = resolveLinks ? await _linkManager.GetItemLink(path) : null; // bad link detected, just return stub // cause client cannot, for example, delete it if we return NotFound for this item if (ulink != null && ulink.IsBad) { var res = ulink.ToBadEntry(); _itemCache.Add(res.FullPath, res); return(res); } if (itemType == ItemType.Unknown && ulink != null) { itemType = ulink.ItemType; } // TODO: cache (parent) folder for file //if (itemType == ItemType.File) //{ // var cachefolder = datares.ToFolder(path, ulink); // _itemCache.Add(cachefolder.FullPath, cachefolder); // //_itemCache.Add(cachefolder.Files); //} var entry = await Account.RequestRepo.FolderInfo(path, ulink); if (null == entry) { return(null); } if (itemType == ItemType.Unknown) { itemType = entry is Folder ? ItemType.Folder : ItemType.File; } // fill folder with links if any if (itemType == ItemType.Folder && entry is Folder folder) { var flinks = _linkManager.GetItems(folder.FullPath); if (flinks.Any()) { //var z = flinks.Where(f => f.IsFile).Skip(1).ToList(); // Take(15).ToList(); 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) { PublicLink = flink.Href, }; if (flink.CreationDate != null) { newfile.LastWriteTimeUtc = flink.CreationDate.Value; } folder.Files.Add(newfile); } } } } } _itemCache.Add(entry.FullPath, entry); if (entry is Folder cfolder) { _itemCache.Add(cfolder.Files.Select(f => new KeyValuePair <string, IEntry>(f.FullPath, f))); } return(entry); }
/// <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); 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(); if (copyRes.status != 200) { return(false); } if (doRename || WebDavPath.Name(copyRes.body) != newname) { string newFullPath = WebDavPath.Combine(destPath, WebDavPath.Name(copyRes.body)); 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); }
public MailruStoreItem(ILockingManager lockingManager, File fileInfo, bool isWritable) { LockingManager = lockingManager; _fileInfo = fileInfo; IsWritable = isWritable; }
///// <summary> ///// Get list of files and folders from account. ///// </summary> ///// <param name="path">Path in the cloud to return the list of the items.</param> ///// <param name="itemType">Unknown, File/Folder if you know for sure</param> ///// <param name="resolveLinks">True if you know for sure that's not a linked item</param> ///// <returns>List of the items.</returns> public virtual async Task <IEntry> GetItemAsync(string path, ItemType itemType = ItemType.Unknown, bool resolveLinks = true) { //TODO: вообще, всё плохо стало, всё запуталось, всё надо переписать var uriMatch = Regex.Match(path, @"\A/https://cloud\.mail\.\w+/public(?<uri>/\S+/\S+(/.*)?)\Z"); if (uriMatch.Success) { return(await GetPublicItemAsync(uriMatch.Groups["uri"].Value, itemType)); } if (Account.IsAnonymous) { return(null); } path = WebDavPath.Clean(path); if (_settings.CacheListingSec > 0) { var cached = CacheGetEntry(path, _settings.ListDepth); if (cached != null) { return(cached); } } //TODO: subject to refact!!! var ulink = resolveLinks ? await _linkManager.GetItemLink(path) : null; // bad link detected, just return stub // cause client cannot, for example, delete it if we return NotFound for this item if (ulink != null && ulink.IsBad) { var res = ulink.ToBadEntry(); _itemCache.Add(res.FullPath, res); return(res); } if (itemType == ItemType.Unknown && ulink != null) { itemType = ulink.ItemType; } // TODO: cache (parent) folder for file //if (itemType == ItemType.File) //{ // var cachefolder = datares.ToFolder(path, ulink); // _itemCache.Add(cachefolder.FullPath, cachefolder); // //_itemCache.Add(cachefolder.Files); //} var entry = await Account.RequestRepo.FolderInfo(path, ulink); if (null == entry) { return(null); } if (itemType == ItemType.Unknown) { itemType = entry is Folder ? ItemType.Folder : ItemType.File; } // fill folder with links if any if (itemType == ItemType.Folder && entry is Folder folder) { var flinks = _linkManager.GetItems(folder.FullPath); if (flinks.Any()) { //var z = flinks.Where(f => f.IsFile).Skip(1).ToList(); // Take(15).ToList(); 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) { PublicLink = flink.Href, }; if (flink.CreationDate != null) { newfile.LastWriteTimeUtc = flink.CreationDate.Value; } folder.Files.Add(newfile); } } } } } if (_settings.CacheListingSec > 0) { CacheAddEntry(entry); } return(entry); }