/// <summary> /// Gets a file listing from the server asynchronously. Each <see cref="FtpListItem"/> object returned /// contains information about the file that was able to be retrieved. /// </summary> /// <remarks> /// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the /// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property /// is equal to 0, then it means the size of the object could also not /// be retrieved. /// </remarks> /// <param name="path">The path to list</param> /// <param name="options">Options that dictate how the list operation is performed</param> /// <returns>An array of items retrieved in the listing</returns> public async Task <FtpListItem[]> GetListingAsync(string path, FtpListOption options) { //TODO: Rewrite as true async method with cancellation support return(await Task.Factory.FromAsync <string, FtpListOption, FtpListItem[]>( (p, o, ac, s) => BeginGetListing(p, o, ac, s), ar => EndGetListing(ar), path, options, null)); }
private void CalculateGetListingCommand(string path, FtpListOption options, out string listcmd, out bool machineList) { // read flags var isForceList = (options & FtpListOption.ForceList) == FtpListOption.ForceList; var isNoPath = (options & FtpListOption.NoPath) == FtpListOption.NoPath; var isNameList = (options & FtpListOption.NameList) == FtpListOption.NameList; var isUseLS = (options & FtpListOption.UseLS) == FtpListOption.UseLS; var isAllFiles = (options & FtpListOption.AllFiles) == FtpListOption.AllFiles; var isRecursive = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList; machineList = false; // use machine listing if supported by the server if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) { listcmd = "MLSD"; machineList = true; } else { // otherwise use one of the legacy name listing commands if (isUseLS) { listcmd = "LS"; } else if (isNameList) { listcmd = "NLST"; } else { var listopts = ""; listcmd = "LIST"; // add option flags if (isAllFiles) { listopts += "a"; } if (isRecursive) { listopts += "R"; } if (listopts.Length > 0) { listcmd += " -" + listopts; } } } if (!isNoPath) { listcmd = listcmd + " " + path.GetFtpPath(); } }
/// <summary> /// Asynchronously removes a directory and all its contents. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <param name="token">The token that can be used to cancel the entire process</param> public Task DeleteDirectoryAsync(string path, FtpListOption options, CancellationToken token = default(CancellationToken)) { // verify args if (path.IsBlank()) { throw new ArgumentException("Required parameter is null or blank.", "path"); } LogFunc(nameof(DeleteDirectoryAsync), new object[] { path, options }); return(DeleteDirInternalAsync(path, true, options, token)); }
/// <summary> /// Deletes the specified directory and all its contents. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example> public void DeleteDirectory(string path, FtpListOption options) { // verify args if (path.IsBlank()) { throw new ArgumentException("Required parameter is null or blank.", "path"); } LogFunc(nameof(DeleteDirectory), new object[] { path, options }); DeleteDirInternal(path, true, options); }
/// <summary> /// Begins an asynchronous operation to delete the specified directory and all its contents. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <param name="callback">Async callback</param> /// <param name="state">State object</param> /// <returns>IAsyncResult</returns> /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example> public IAsyncResult BeginDeleteDirectory(string path, FtpListOption options, AsyncCallback callback, object state) { AsyncDeleteDirectory func; IAsyncResult ar; lock (m_asyncmethods) { ar = (func = DeleteDirectory).BeginInvoke(path, options, callback, state); m_asyncmethods.Add(ar, func); } return(ar); }
/// <summary> /// Gets a file listing from the server asynchronously /// </summary> /// <param name="path">The path to list</param> /// <param name="options">Options that dictate how the list operation is performed</param> /// <param name="callback">AsyncCallback method</param> /// <param name="state">State object</param> /// <returns>IAsyncResult</returns> /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example> public IAsyncResult BeginGetListing(string path, FtpListOption options, AsyncCallback callback, Object state) { IAsyncResult ar; AsyncGetListing func; lock (m_asyncmethods) { ar = (func = new AsyncGetListing(GetListing)).BeginInvoke(path, options, callback, state); m_asyncmethods.Add(ar, func); } return(ar); }
/// <summary> /// Checks if a file exsts on the server by taking a /// file listing of the parent directory in the path /// and comparing the results the path supplied. /// </summary> /// <param name="path">The full or relative path to the file</param> /// <param name="options">Options for controling the file listing used to /// determine if the file exists.</param> /// <returns>True if the file exists</returns> public async Task <bool> FileExistsAsync(string path, FtpListOption options = 0) { string dirname = path.GetFtpDirectoryName(); if (!(await DirectoryExistsAsync(dirname))) { return(false); } var directoryListing = await GetListingAsync(dirname); if (directoryListing.Any(item => item.Type == FtpFileSystemObjectType.File && item.Name.GetFtpFileName() == path.GetFtpFileName())) { return(true); } return(false); }
/// <summary> /// Recursive method of GetListingAsync, to recurse through directories on servers that do not natively support recursion. /// Automatically called by GetListingAsync where required. /// Uses flat recursion instead of head recursion. /// </summary> /// <param name="path">The path of the directory to list</param> /// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param> /// <returns>An array of FtpListItem objects</returns> protected async Task <FtpListItem[]> GetListingRecursiveAsync(string path, FtpListOption options) { // remove the recursive flag options &= ~FtpListOption.Recursive; // add initial path to list of folders to explore var stack = new Stack <string>(); stack.Push(path); var allFiles = new List <FtpListItem>(); // explore folders while (stack.Count > 0) { // get path of folder to list var currentPath = stack.Pop(); if (!currentPath.EndsWith("/")) { currentPath += "/"; } // list it FtpListItem[] items = await GetListingAsync(currentPath, options); // add it to the final listing allFiles.AddRange(items); // extract the directories foreach (var item in items) { if (item.Type == FtpFileSystemObjectType.Directory) { stack.Push(item.FullName); } } items = null; // recurse } // final list of all files and dirs return(allFiles.ToArray()); }
/// <summary> /// Получение файлов с проверкой имени делегатом пропуска файлов /// </summary> public int GetFiles(SkipFilesDelegate skipFiles, string destDir, FtpListOption options) { int count = 0; if (ftpClient.IsConnected) { FtpListItem[] items = GetFtpItemsList(Path.AltDirectorySeparatorChar + WorkDir, options); foreach (FtpListItem itemData in items) { if (!skipFiles(itemData.Name)) { string path = itemData.FullName.Replace(WorkDir, string.Empty).Trim('/').Replace('/', Path.DirectorySeparatorChar); GetFile(itemData.FullName, Path.Combine(destDir, path)); count++; } } } return(count); }
/// <summary> /// Формирование списка файлов на ftp /// </summary> private FtpListItem[] GetFtpItemsList(string topDirectory, FtpListOption options) { List <FtpListItem> files = new List <FtpListItem>(); FtpListItem[] items = ftpClient.GetListing(topDirectory); foreach (FtpListItem item in items) { if (item.Type != FtpFileSystemObjectType.Directory) { files.Add(item); } else if (options == FtpListOption.Recursive) { if (!item.Name.IsMatch(@"^\.+$")) { files.AddRange(GetFtpItemsList(topDirectory + Path.AltDirectorySeparatorChar + item.Name, options)); } } } return(files.ToArray()); }
/// <summary> /// Deletes the directory asynchronous. /// </summary> /// <typeparam name="TFtpClient">The type of the FTP client.</typeparam> /// <param name="client">The client.</param> /// <param name="path">The path.</param> /// <param name="force">if set to <c>true</c> [force].</param> /// <param name="options">The options.</param> /// <param name="factory">The factory.</param> /// <param name="creationOptions">The creation options.</param> /// <param name="scheduler">The scheduler.</param> /// <returns></returns> public static Task DeleteDirectoryAsync <TFtpClient>(this TFtpClient client, string path, bool force, FtpListOption options, TaskFactory factory = null, TaskCreationOptions creationOptions = default(TaskCreationOptions), TaskScheduler scheduler = null) where TFtpClient : IFtpClient { return((factory = factory ?? Task.Factory).FromAsync( client.BeginDeleteDirectory(path, force, options, null, null), client.EndDeleteDirectory, creationOptions, scheduler ?? factory.Scheduler ?? TaskScheduler.Current)); }
/// <summary> /// Files the exists asynchronous. /// </summary> /// <typeparam name="TFtpClient">The type of the FTP client.</typeparam> /// <param name="client">The client.</param> /// <param name="path">The path.</param> /// <param name="options">The options.</param> /// <param name="factory">The factory.</param> /// <param name="creationOptions">The creation options.</param> /// <param name="scheduler">The scheduler.</param> /// <returns></returns> public static Task <bool> FileExistsAsync <TFtpClient>(this TFtpClient client, string path, FtpListOption options, TaskFactory <bool> factory = null, TaskCreationOptions creationOptions = default(TaskCreationOptions), TaskScheduler scheduler = null) where TFtpClient : IFtpClient { return((factory = factory ?? Task <bool> .Factory).FromAsync( client.BeginFileExists(path, options, null, null), client.EndFileExists, creationOptions, scheduler ?? factory.Scheduler ?? TaskScheduler.Current)); }
/// <summary> /// Gets the listing asynchronous. /// </summary> /// <typeparam name="TFtpClient">The type of the FTP client.</typeparam> /// <param name="client">The client.</param> /// <param name="path">The path.</param> /// <param name="options">The options.</param> /// <param name="factory">The factory.</param> /// <param name="creationOptions">The creation options.</param> /// <param name="scheduler">The scheduler.</param> /// <returns></returns> public static Task <FtpListItem[]> GetListingAsync <TFtpClient>(this TFtpClient client, string path, FtpListOption options, TaskFactory <FtpListItem[]> factory = null, TaskCreationOptions creationOptions = default(TaskCreationOptions), TaskScheduler scheduler = null) where TFtpClient : IFtpClient { return((factory = factory ?? Task <FtpListItem[]> .Factory).FromAsync( client.BeginGetListing(path, options, null, null), client.EndGetListing, creationOptions, scheduler ?? factory.Scheduler ?? TaskScheduler.Current)); }
/// <summary> /// Checks whether <see cref="o:GetListing"/> will be called recursively or not. /// </summary> /// <param name="options"></param> /// <returns></returns> private bool WasGetListingRecursive(FtpListOption options) { // FIX: GetListing() now supports recursive listing for all types of lists (name list, file list, machine list) // even if the server does not support recursive listing, because it does its own internal recursion. return((options & FtpListOption.Recursive) == FtpListOption.Recursive); }
/// <summary> /// Asynchronously removes a directory. Used by <see cref="DeleteDirectoryAsync(string)"/> and /// <see cref="DeleteDirectoryAsync(string, FtpListOption)"/>. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="deleteContents">Delete the contents before deleting the folder</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <param name="token">The token that can be used to cancel the entire process</param> /// <returns></returns> private async Task DeleteDirInternalAsync(string path, bool deleteContents, FtpListOption options, CancellationToken token = default(CancellationToken)) { FtpReply reply; path = path.GetFtpPath(); // server-specific directory deletion if (!path.IsFtpRootDirectory()) { // ask the server handler to delete a directory if (ServerHandler != null) { if (await ServerHandler.DeleteDirectoryAsync(this, path, path, deleteContents, options, token)) { return; } } } // DELETE CONTENTS OF THE DIRECTORY if (deleteContents) { // when GetListing is called with recursive option, then it does not // make any sense to call another DeleteDirectory with force flag set. // however this requires always delete files first. var recurse = !WasGetListingRecursive(options); // items that are deeper in directory tree are listed first, // then files will be listed before directories. This matters // only if GetListing was called with recursive option. FtpListItem[] itemList; if (recurse) { itemList = await GetListingAsync(path, options, token); } else { itemList = (await GetListingAsync(path, options, token)).OrderByDescending(x => x.FullName.Count(c => c.Equals('/'))).ThenBy(x => x.Type).ToArray(); } // delete the item based on the type foreach (var item in itemList) { switch (item.Type) { case FtpFileSystemObjectType.File: await DeleteFileAsync(item.FullName, token); break; case FtpFileSystemObjectType.Directory: await DeleteDirInternalAsync(item.FullName, recurse, options, token); break; default: throw new FtpException("Don't know how to delete object type: " + item.Type); } } } // SKIP DELETING ROOT DIRS // can't delete the working directory and // can't delete the server root. if (path.IsFtpRootDirectory()) { return; } // DELETE ACTUAL DIRECTORY if (!(reply = await ExecuteAsync("RMD " + path, token)).Success) { throw new FtpCommandException(reply); } }
/// <summary> /// Perform server-specific delete directory commands here. /// Return true if you executed a server-specific command. /// </summary> public override bool DeleteDirectory(FtpClient client, string path, string ftppath, bool deleteContents, FtpListOption options) { // Support #378 - Support RMDIR command for ProFTPd if (deleteContents && client.HasFeature(FtpCapability.SITE_RMDIR)) { if ((client.Execute("SITE RMDIR " + ftppath)).Success) { client.LogStatus(FtpTraceLevel.Verbose, "Used the server-specific SITE RMDIR command to quickly delete directory: " + ftppath); return(true); } else { client.LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific SITE RMDIR command to quickly delete directory: " + ftppath); } } return(false); }
private bool ServerDeleteDirectory(string path, string ftppath, bool deleteContents, FtpListOption options) { FtpReply reply; // Support #378 - Support RMDIR command for ProFTPd if (deleteContents && ServerType == FtpServer.ProFTPD && HasFeature(FtpCapability.SITE_RMDIR)) { if ((reply = Execute("SITE RMDIR " + ftppath)).Success) { this.LogStatus(FtpTraceLevel.Verbose, "Used the server-specific SITE RMDIR command to quickly delete: " + ftppath); return(true); } else { this.LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific SITE RMDIR command to quickly delete: " + ftppath); } } return(false); }
/// <summary> /// Получение файлов с проверкой имени делегатом пропуска файлов и делегатом получения пути-назначения файла /// </summary> public int GetFiles(SkipFilesDelegate skipFiles, DestinationDirectoryFilesDelegate destDir, FtpListOption options) { int count = 0; if (ftpClient.IsConnected) { FtpListItem[] items = ftpClient.GetListing(Path.AltDirectorySeparatorChar + WorkDir, options); foreach (FtpListItem itemData in items) { if (!skipFiles(itemData.FullName)) { string outDirectory = destDir(itemData.Name); if (!Directory.Exists(outDirectory)) { Directory.CreateDirectory(outDirectory); } GetFile(itemData.FullName, Path.Combine(outDirectory, itemData.Name)); count++; } } } return(count); }
/// <summary> /// Perform async server-specific delete directory commands here. /// Return true if you executed a server-specific command. /// </summary> public virtual async Task <bool> DeleteDirectoryAsync(FtpClient client, string path, string ftppath, bool deleteContents, FtpListOption options, CancellationToken token) { return(false); }
/// <summary> /// Checks if a file exsts on the server by taking a /// file listing of the parent directory in the path /// and comparing the results the path supplied. /// </summary> /// <param name="path">The full or relative path to the file</param> /// <param name="options">Options for controling the file listing used to /// determine if the file exists.</param> /// <returns>True if the file exists</returns> public async Task<bool> FileExistsAsync(string path, FtpListOption options = 0) { string dirname = path.GetFtpDirectoryName(); if (!(await DirectoryExistsAsync(dirname))) return false; var directoryListing = await GetListingAsync(dirname); if ( directoryListing.Any( item => item.Type == FtpFileSystemObjectType.File && item.Name.GetFtpFileName() == path.GetFtpFileName())) { return true; } return false; }
/// <summary> /// Gets a file listing from the server asynchronously. Each <see cref="FtpListItem"/> object returned /// contains information about the file that was able to be retrieved. /// </summary> /// <remarks> /// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the /// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property /// is equal to 0, then it means the size of the object could also not /// be retrieved. /// </remarks> /// <param name="path">The path to list</param> /// <param name="options">Options that dictate how the list operation is performed</param> /// <param name="token">Cancellation Token</param> /// <returns>An array of items retrieved in the listing</returns> public async Task <FtpListItem[]> GetListingAsync(string path, FtpListOption options, CancellationToken token = default(CancellationToken)) { // start recursive process if needed and unsupported by the server if ((options & FtpListOption.Recursive) == FtpListOption.Recursive && !RecursiveList) { return(await GetListingRecursiveAsync(GetAbsolutePath(path), options)); } this.LogFunc(nameof(GetListingAsync), new object[] { path, options }); FtpListItem item = null; List <FtpListItem> lst = new List <FtpListItem>(); List <string> rawlisting = new List <string>(); string listcmd = null; string buf = null; // read flags bool isIncludeSelf = (options & FtpListOption.IncludeSelfAndParent) == FtpListOption.IncludeSelfAndParent; bool isForceList = (options & FtpListOption.ForceList) == FtpListOption.ForceList; bool isNoPath = (options & FtpListOption.NoPath) == FtpListOption.NoPath; bool isNameList = (options & FtpListOption.NameList) == FtpListOption.NameList; bool isUseLS = (options & FtpListOption.UseLS) == FtpListOption.UseLS; bool isAllFiles = (options & FtpListOption.AllFiles) == FtpListOption.AllFiles; bool isRecursive = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList; bool isDerefLinks = (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks; bool isGetModified = (options & FtpListOption.Modify) == FtpListOption.Modify; bool isGetSize = (options & FtpListOption.Size) == FtpListOption.Size; // calc path to request path = await GetAbsolutePathAsync(path, token); // MLSD provides a machine readable format with 100% accurate information // so always prefer MLSD over LIST unless the caller of this method overrides it with the ForceList option bool machineList = false; if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) { listcmd = "MLSD"; machineList = true; } else { if (isUseLS) { listcmd = "LS"; } else if (isNameList) { listcmd = "NLST"; } else { string listopts = ""; listcmd = "LIST"; if (isAllFiles) { listopts += "a"; } if (isRecursive) { listopts += "R"; } if (listopts.Length > 0) { listcmd += " -" + listopts; } } } if (!isNoPath) { listcmd = (listcmd + " " + path.GetFtpPath()); } await ExecuteAsync("TYPE I", token); // read in raw file listing try { using (FtpDataStream stream = await OpenDataStreamAsync(listcmd, 0, token)) { try { this.LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+"); if (this.BulkListing) { // increases performance of GetListing by reading multiple lines of the file listing at once foreach (var line in await stream.ReadAllLinesAsync(Encoding, this.BulkListingLength, token)) { if (!FtpExtensions.IsNullOrWhiteSpace(line)) { rawlisting.Add(line); this.LogLine(FtpTraceLevel.Verbose, "Listing: " + line); } } } else { // GetListing will read file listings line-by-line (actually byte-by-byte) while ((buf = await stream.ReadLineAsync(Encoding, token)) != null) { if (buf.Length > 0) { rawlisting.Add(buf); this.LogLine(FtpTraceLevel.Verbose, "Listing: " + buf); } } } this.LogLine(FtpTraceLevel.Verbose, "-----------------------------------------"); } finally { stream.Close(); } } } catch (FtpMissingSocketException) { // Some FTP server does not send any response when listing an empty directory // and the authentication fails, if no communication socket is provided by the server } for (int i = 0; i < rawlisting.Count; i++) { buf = rawlisting[i]; if (isNameList) { // if NLST was used we only have a file name so // there is nothing to parse. item = new FtpListItem() { FullName = buf }; if (await DirectoryExistsAsync(item.FullName, token)) { item.Type = FtpFileSystemObjectType.Directory; } else { item.Type = FtpFileSystemObjectType.File; } lst.Add(item); } else { // if this is a result of LIST -R then the path will be spit out // before each block of objects if (listcmd.StartsWith("LIST") && isRecursive) { if (buf.StartsWith("/") && buf.EndsWith(":")) { path = buf.TrimEnd(':'); continue; } } // if the next line in the listing starts with spaces // it is assumed to be a continuation of the current line if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" "))) { buf += rawlisting[++i]; } try { item = m_listParser.ParseSingleLine(path, buf, m_caps, machineList); } catch (FtpListParser.CriticalListParseException) { this.LogStatus(FtpTraceLevel.Verbose, "Restarting parsing from first entry in list"); i = -1; lst.Clear(); continue; } // FtpListItem.Parse() returns null if the line // could not be parsed if (item != null) { if (isIncludeSelf || !(item.Name == "." || item.Name == "..")) { lst.Add(item); } else { //this.LogStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name); } } else { this.LogStatus(FtpTraceLevel.Warn, "Failed to parse file listing: " + buf); } } // load extended information that wasn't available if the list options flags say to do so. if (item != null) { // try to dereference symbolic links if the appropriate list // option was passed if (item.Type == FtpFileSystemObjectType.Link && isDerefLinks) { item.LinkObject = await DereferenceLinkAsync(item, token); } // if need to get file modified date if (isGetModified && HasFeature(FtpCapability.MDTM)) { // if the modified date was not loaded or the modified date is more than a day in the future // and the server supports the MDTM command, load the modified date. // most servers do not support retrieving the modified date // of a directory but we try any way. if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) { DateTime modify; if (item.Type == FtpFileSystemObjectType.Directory) { this.LogStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this..."); } if ((modify = await GetModifiedTimeAsync(item.FullName, token: token)) != DateTime.MinValue) { item.Modified = modify; } } } // if need to get file size if (isGetSize && HasFeature(FtpCapability.SIZE)) { // if no size was parsed, the object is a file and the server // supports the SIZE command, then load the file size if (item.Size == -1) { if (item.Type != FtpFileSystemObjectType.Directory) { item.Size = await GetFileSizeAsync(item.FullName, token); } else { item.Size = 0; } } } } } return(lst.ToArray()); }
private bool ServerDeleteDirectory(string path, string ftppath, bool deleteContents, FtpListOption options) { // Support #378 - Support RMDIR command for ProFTPd if (deleteContents && HasFeature(FtpCapability.SITE_RMDIR)) { if ((Execute("SITE RMDIR " + ftppath)).Success) { LogStatus(FtpTraceLevel.Verbose, "Used the server-specific SITE RMDIR command to quickly delete directory: " + ftppath); return(true); } else { LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific SITE RMDIR command to quickly delete directory: " + ftppath); } } // Support #88 - Support RMDA command for Serv-U if (deleteContents && HasFeature(FtpCapability.RMDA)) { if ((Execute("RMDA " + ftppath)).Success) { LogStatus(FtpTraceLevel.Verbose, "Used the server-specific RMDA command to quickly delete directory: " + ftppath); return(true); } else { LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific RMDA command to quickly delete directory: " + ftppath); } } return(false); }
/// <summary> /// Perform async server-specific delete directory commands here. /// Return true if you executed a server-specific command. /// </summary> public override async Task <bool> DeleteDirectoryAsync(FtpClient client, string path, string ftppath, bool deleteContents, FtpListOption options, CancellationToken token) { // Support #88 - Support RMDA command for Serv-U if (deleteContents && client.HasFeature(FtpCapability.RMDA)) { if ((await client.ExecuteAsync("RMDA " + ftppath, token)).Success) { client.LogStatus(FtpTraceLevel.Verbose, "Used the server-specific RMDA command to quickly delete directory: " + ftppath); return(true); } else { client.LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific RMDA command to quickly delete directory: " + ftppath); } } return(false); }
/// <summary> /// Deletes the specified directory and all its contents. /// </summary> /// <param name="path">The full or relative path of the directory to delete</param> /// <param name="deleteContents">If the directory is not empty, remove its contents</param> /// <param name="options">Useful to delete hidden files or dot-files.</param> /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example> private void DeleteDirInternal(string path, bool deleteContents, FtpListOption options) { FtpReply reply; var ftppath = path.GetFtpPath(); #if !CORE14 lock (m_lock) { #endif // server-specific directory deletion if (!ftppath.IsFtpRootDirectory()) { if (FtpServerSpecificHandler.ServerDeleteDirectory(this, path, ftppath, deleteContents, options)) { return; } } // DELETE CONTENTS OF THE DIRECTORY if (deleteContents) { // when GetListing is called with recursive option, then it does not // make any sense to call another DeleteDirectory with force flag set. // however this requires always delete files first. var recurse = !WasGetListingRecursive(options); // items that are deeper in directory tree are listed first, // then files will be listed before directories. This matters // only if GetListing was called with recursive option. FtpListItem[] itemList; if (recurse) { itemList = GetListing(path, options); } else { itemList = GetListing(path, options).OrderByDescending(x => x.FullName.Count(c => c.Equals('/'))).ThenBy(x => x.Type).ToArray(); } // delete the item based on the type foreach (var item in itemList) { switch (item.Type) { case FtpFileSystemObjectType.File: DeleteFile(item.FullName); break; case FtpFileSystemObjectType.Directory: DeleteDirInternal(item.FullName, recurse, options); break; default: throw new FtpException("Don't know how to delete object type: " + item.Type); } } } // SKIP DELETING ROOT DIRS // can't delete the working directory and // can't delete the server root. if (ftppath.IsFtpRootDirectory()) { return; } // DELETE ACTUAL DIRECTORY if (!(reply = Execute("RMD " + ftppath)).Success) { throw new FtpCommandException(reply); } #if !CORE14 } #endif }
/// <summary> /// Gets a file listing from the server. Each <see cref="FtpListItem"/> object returned /// contains information about the file that was able to be retrieved. /// </summary> /// <remarks> /// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the /// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property /// is equal to 0, then it means the size of the object could also not /// be retrieved. /// </remarks> /// <param name="path">The path of the directory to list</param> /// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param> /// <returns>An array of FtpListItem objects</returns> /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example> public FtpListItem[] GetListing(string path, FtpListOption options) { FtpTrace.WriteFunc("GetListing", new object[] { path, options }); FtpListItem item = null; List <FtpListItem> lst = new List <FtpListItem>(); List <string> rawlisting = new List <string>(); string listcmd = null; string buf = null; // read flags bool isIncludeSelf = (options & FtpListOption.IncludeSelfAndParent) == FtpListOption.IncludeSelfAndParent; bool isForceList = (options & FtpListOption.ForceList) == FtpListOption.ForceList; bool isNoPath = (options & FtpListOption.NoPath) == FtpListOption.NoPath; bool isNameList = (options & FtpListOption.NameList) == FtpListOption.NameList; bool isUseLS = (options & FtpListOption.UseLS) == FtpListOption.UseLS; bool isAllFiles = (options & FtpListOption.AllFiles) == FtpListOption.AllFiles; bool isRecursive = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList; bool isDerefLinks = (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks; bool isGetModified = (options & FtpListOption.Modify) == FtpListOption.Modify; bool isGetSize = (options & FtpListOption.Size) == FtpListOption.Size; // calc path to request path = GetAbsolutePath(path); // MLSD provides a machine readable format with 100% accurate information // so always prefer MLSD over LIST unless the caller of this method overrides it with the ForceList option bool machineList = false; if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) { listcmd = "MLSD"; machineList = true; } else { if (isUseLS) { listcmd = "LS"; } else if (isNameList) { listcmd = "NLST"; } else { string listopts = ""; listcmd = "LIST"; if (isAllFiles) { listopts += "a"; } if (isRecursive) { listopts += "R"; } if (listopts.Length > 0) { listcmd += " -" + listopts; } } } if (!isNoPath) { listcmd = (listcmd + " " + path.GetFtpPath()); } #if !CORE14 lock (m_lock) { #endif Execute("TYPE I"); // read in raw file listing using (FtpDataStream stream = OpenDataStream(listcmd, 0)) { try { FtpTrace.WriteLine(FtpTraceLevel.Verbose, "+---------------------------------------+"); while ((buf = stream.ReadLine(Encoding)) != null) { if (buf.Length > 0) { rawlisting.Add(buf); FtpTrace.WriteLine(FtpTraceLevel.Verbose, "Listing: " + buf); } } FtpTrace.WriteLine(FtpTraceLevel.Verbose, "-----------------------------------------"); } finally { stream.Close(); } } #if !CORE14 } #endif for (int i = 0; i < rawlisting.Count; i++) { buf = rawlisting[i]; if (isNameList) { // if NLST was used we only have a file name so // there is nothing to parse. item = new FtpListItem() { FullName = buf }; if (DirectoryExists(item.FullName)) { item.Type = FtpFileSystemObjectType.Directory; } else { item.Type = FtpFileSystemObjectType.File; } lst.Add(item); } else { // if this is a result of LIST -R then the path will be spit out // before each block of objects if (listcmd.StartsWith("LIST") && isRecursive) { if (buf.StartsWith("/") && buf.EndsWith(":")) { path = buf.TrimEnd(':'); continue; } } // if the next line in the listing starts with spaces // it is assumed to be a continuation of the current line if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" "))) { buf += rawlisting[++i]; } item = m_listParser.ParseSingleLine(path, buf, m_caps, machineList); // FtpListItem.Parse() returns null if the line // could not be parsed if (item != null) { if (isIncludeSelf || !(item.Name == "." || item.Name == "..")) { lst.Add(item); } else { //FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name); } } else { FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse file listing: " + buf); } } // load extended information that wasn't available if the list options flags say to do so. if (item != null) { // try to dereference symbolic links if the appropriate list // option was passed if (item.Type == FtpFileSystemObjectType.Link && isDerefLinks) { item.LinkObject = DereferenceLink(item); } // if need to get file modified date if (isGetModified && HasFeature(FtpCapability.MDTM)) { // if the modified date was not loaded or the modified date is more than a day in the future // and the server supports the MDTM command, load the modified date. // most servers do not support retrieving the modified date // of a directory but we try any way. if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) { DateTime modify; if (item.Type == FtpFileSystemObjectType.Directory) { FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this..."); } if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue) { item.Modified = modify; } } } // if need to get file size if (isGetSize && HasFeature(FtpCapability.SIZE)) { // if no size was parsed, the object is a file and the server // supports the SIZE command, then load the file size if (item.Size == -1) { if (item.Type != FtpFileSystemObjectType.Directory) { item.Size = GetFileSize(item.FullName); } else { item.Size = 0; } } } } } return(lst.ToArray()); }
private async Task <bool> ServerDeleteDirectoryAsync(string path, string ftppath, bool deleteContents, FtpListOption options, CancellationToken token) { // Support #378 - Support RMDIR command for ProFTPd if (deleteContents && ServerType == FtpServer.ProFTPD && HasFeature(FtpCapability.SITE_RMDIR)) { if ((await ExecuteAsync("SITE RMDIR " + ftppath, token)).Success) { LogStatus(FtpTraceLevel.Verbose, "Used the server-specific SITE RMDIR command to quickly delete: " + ftppath); return(true); } else { LogStatus(FtpTraceLevel.Verbose, "Failed to use the server-specific SITE RMDIR command to quickly delete: " + ftppath); } } return(false); }
/// <summary> /// Gets a file listing from the server asynchronously. Each <see cref="FtpListItem"/> object returned /// contains information about the file that was able to be retrieved. /// </summary> /// <remarks> /// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the /// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property /// is equal to 0, then it means the size of the object could also not /// be retrieved. /// </remarks> /// <param name="path">The path to list</param> /// <param name="options">Options that dictate how the list operation is performed</param> /// <param name="token">Cancellation Token</param> /// <returns>An array of items retrieved in the listing</returns> public async Task <FtpListItem[]> GetListingAsync(string path, FtpListOption options, CancellationToken token = default(CancellationToken)) { // start recursive process if needed and unsupported by the server if ((options & FtpListOption.Recursive) == FtpListOption.Recursive && !RecursiveList) { return(await GetListingRecursiveAsync(GetAbsolutePath(path), options)); } this.LogFunc(nameof(GetListingAsync), new object[] { path, options }); FtpListItem item = null; List <FtpListItem> lst = new List <FtpListItem>(); List <string> rawlisting = new List <string>(); string listcmd = null; string buf = null; // read flags bool isIncludeSelf = (options & FtpListOption.IncludeSelfAndParent) == FtpListOption.IncludeSelfAndParent; bool isNameList = (options & FtpListOption.NameList) == FtpListOption.NameList; bool isRecursive = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList; bool isDerefLinks = (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks; bool isGetModified = (options & FtpListOption.Modify) == FtpListOption.Modify; bool isGetSize = (options & FtpListOption.Size) == FtpListOption.Size; // calc path to request path = await GetAbsolutePathAsync(path, token); // MLSD provides a machine readable format with 100% accurate information // so always prefer MLSD over LIST unless the caller of this method overrides it with the ForceList option bool machineList; CalculateGetListingCommand(path, options, out listcmd, out machineList); // read in raw file listing rawlisting = await GetListingInternalAsync(listcmd, true, token); for (int i = 0; i < rawlisting.Count; i++) { buf = rawlisting[i]; if (isNameList) { // if NLST was used we only have a file name so // there is nothing to parse. item = new FtpListItem() { FullName = buf }; if (await DirectoryExistsAsync(item.FullName, token)) { item.Type = FtpFileSystemObjectType.Directory; } else { item.Type = FtpFileSystemObjectType.File; } lst.Add(item); } else { // if this is a result of LIST -R then the path will be spit out // before each block of objects if (listcmd.StartsWith("LIST") && isRecursive) { if (buf.StartsWith("/") && buf.EndsWith(":")) { path = buf.TrimEnd(':'); continue; } } // if the next line in the listing starts with spaces // it is assumed to be a continuation of the current line if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" "))) { buf += rawlisting[++i]; } try { item = m_listParser.ParseSingleLine(path, buf, m_capabilities, machineList); } catch (FtpListParseException) { this.LogStatus(FtpTraceLevel.Verbose, "Restarting parsing from first entry in list"); i = -1; lst.Clear(); continue; } // FtpListItem.Parse() returns null if the line // could not be parsed if (item != null) { if (isIncludeSelf || !(item.Name == "." || item.Name == "..")) { lst.Add(item); } else { //this.LogStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name); } } else { this.LogStatus(FtpTraceLevel.Warn, "Failed to parse file listing: " + buf); } } // load extended information that wasn't available if the list options flags say to do so. if (item != null) { // try to dereference symbolic links if the appropriate list // option was passed if (item.Type == FtpFileSystemObjectType.Link && isDerefLinks) { item.LinkObject = await DereferenceLinkAsync(item, token); } // if need to get file modified date if (isGetModified && HasFeature(FtpCapability.MDTM)) { // if the modified date was not loaded or the modified date is more than a day in the future // and the server supports the MDTM command, load the modified date. // most servers do not support retrieving the modified date // of a directory but we try any way. if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) { DateTime modify; if (item.Type == FtpFileSystemObjectType.Directory) { this.LogStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this..."); } if ((modify = await GetModifiedTimeAsync(item.FullName, token: token)) != DateTime.MinValue) { item.Modified = modify; } } } // if need to get file size if (isGetSize && HasFeature(FtpCapability.SIZE)) { // if no size was parsed, the object is a file and the server // supports the SIZE command, then load the file size if (item.Size == -1) { if (item.Type != FtpFileSystemObjectType.Directory) { item.Size = await GetFileSizeAsync(item.FullName, token); } else { item.Size = 0; } } } } } return(lst.ToArray()); }
/// <summary> /// Perform server-specific delete directory commands here. /// Return true if you executed a server-specific command. /// </summary> public virtual bool DeleteDirectory(FtpClient client, string path, string ftppath, bool deleteContents, FtpListOption options) { return(false); }
/// <summary> /// Получить файлы с сервера по маске - регулярному выражению /// </summary> public int GetFiles(string mask, string destDir, FtpListOption options) { SkipFilesDelegate skipFiles = name => !name.IsMatch(mask); return(GetFiles(skipFiles, destDir, options)); }
public static bool HasFlag(this FtpListOption flags, FtpListOption flag) { return((flags & flag) == flag); }
public static IEnumerable <FtpListItem> GetFileListingRecursiveParallel(this FtpClient client, string startPath, FtpListOption options) { Policy retrier = Policy .Handle <Exception>() .WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15) }); if (options.HasFlag(FtpListOption.Recursive)) { throw new ArgumentException("Do not use recursive option when doing a recursive listing.", "options"); } List <Task <FtpListItem[]> > pending = new List <Task <FtpListItem[]> >(); IEnumerable <FtpListItem> files = new List <FtpListItem>(); Func <string, Task <FtpListItem[]> > listFolderAsync = (string path) => { return(Task.Factory.StartNew(() => { return retrier.Execute(() => { return client.GetListing(path, options); }); })); }; pending.Add(listFolderAsync(startPath)); int completedTaskIndex; while ((completedTaskIndex = Task.WaitAny(pending.ToArray())) != -1 && pending.Count > 0) { var t = pending.ElementAt(completedTaskIndex); pending.RemoveAt(completedTaskIndex); var list = t.Result; foreach (var d in list.Where(x => x.Type == FtpFileSystemObjectType.Directory)) { pending.Add(listFolderAsync(d.FullName)); } files = files.Concat(list.Where(x => x.Type != FtpFileSystemObjectType.Directory).ToList()); } return(files); }