/// <summary>
        /// Download a single file directly.
        /// </summary>
        private async void DownloadFTPFileAsync(
            BackgroundDownloader downloader,
            NetworkCredential credential,
            FTPFileSystem item,
            StorageFile targetFile)
        {
            if (item.Size > 1048576) // 1M Byte
            {
                Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);

                Uri urlWithCredential = item.Url;

                if (credential != null)
                {
                    urlWithCredential = new Uri(item.Url.ToString().ToLower().Replace(@"ftp://",
                        string.Format(@"ftp://{0}:{1}@", 
                        FTPFileSystem.EncodeUrl(credential.UserName), 
                        FTPFileSystem.EncodeUrl(credential.Password))));
                }

                DownloadOperation download = downloader.CreateDownload(
                    urlWithCredential,
                    targetFile);
                ActiveBackgroundDownloaders.Add(download);
                await download.StartAsync().AsTask(progressCallback);
            }
            else
            {
                await FTPClient.DownloadFTPFileAsync(item, targetFile, credential)
                    .ContinueWith(new Action<Task<DownloadCompletedEventArgs>>(DownloadProgress));
            }

        }
        /// <summary>
        /// Download a whole directory.
        /// </summary>
        private async void DownloadFTPDirectoryAsync(
            BackgroundDownloader downloader,
            NetworkCredential credential,
            FTPFileSystem item,
            StorageFolder targetFolder)
        {

            // List the sub folders and files.
            var subItems = await this.ListFtpContentAsync(item.Url, credential);

            // Download the sub folders and files.
            foreach (var subitem in subItems)
            {
                if (subitem.IsDirectory)
                {

                    // Create a local folder.
                    var subFolder = await targetFolder.CreateFolderAsync(subitem.Name,
                        CreationCollisionOption.ReplaceExisting);

                    // Download the whole folder.
                    DownloadFTPDirectoryAsync(downloader, credential, subitem, subFolder);
                }
                else
                {

                    // Create a local file.
                    var file = await targetFolder.CreateFileAsync(subitem.Name,
                        CreationCollisionOption.GenerateUniqueName);

                    // Download the file.
                    DownloadFTPFileAsync(downloader, credential, subitem, file);
                }
            }
        }
        /// <summary>
        /// Get an FTPFileSystem from the recordString.
        /// </summary>
        public static FTPFileSystem ParseRecordString(Uri baseUrl, string recordString, FTPDirectoryListingStyle type)
        {
            FTPFileSystem fileSystem = null;

            if (type == FTPDirectoryListingStyle.UNIX)
            {
                fileSystem = ParseUNIXRecordString(recordString);
            }
            else
            {
                fileSystem = ParseMSDOSRecordString(recordString);
            }

            string encodedName = EncodeUrl(fileSystem.Name);

            // Add "/" to the url if it is a directory
            fileSystem.Url = new Uri(baseUrl, encodedName + (fileSystem.IsDirectory ? "/" : string.Empty));

            return(fileSystem);
        }
        /// <summary>
        /// 12-13-10  12:41PM       <DIR>          Folder A
        /// </summary>
        /// <param name="recordString"></param>
        /// <returns></returns>
        static FTPFileSystem ParseMSDOSRecordString(string recordString)
        {
            FTPFileSystem fileSystem = new FTPFileSystem();

            fileSystem.OriginalRecordString  = recordString.Trim();
            fileSystem.DirectoryListingStyle = FTPDirectoryListingStyle.MSDOS;

            // The segments is like "12-13-10",  "", "12:41PM", "", "","", "",
            // "", "", "<DIR>", "", "", "", "", "", "", "", "", "", "Folder", "A".
            string[] segments = fileSystem.OriginalRecordString.Split(' ');

            int index = 0;

            // The date segment is like "12-13-10" instead of "12-13-2010" if Four-digit years
            // is not checked in IIS.
            string dateSegment = segments[index];

            string[] dateSegments = dateSegment.Split(new char[] { '-' },
                                                      StringSplitOptions.RemoveEmptyEntries);

            int month = int.Parse(dateSegments[0]);
            int day   = int.Parse(dateSegments[1]);
            int year  = int.Parse(dateSegments[2]);

            // If year >=50 and year <100, then  it means the year 19**
            if (year >= 50 && year < 100)
            {
                year += 1900;
            }

            // If year <50, then it means the year 20**
            else if (year < 50)
            {
                year += 2000;
            }

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // The time segment.
            string timesegment = segments[index];

            fileSystem.ModifiedTime = DateTime.Parse(string.Format("{0}-{1}-{2} {3}",
                                                                   year, month, day, timesegment));

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // The size or directory segment.
            // If this segment is "<DIR>", then it means a directory, else it means the
            // file size.
            string sizeOrDirSegment = segments[index];

            fileSystem.IsDirectory = sizeOrDirSegment.Equals("<DIR>",
                                                             StringComparison.OrdinalIgnoreCase);

            // If this fileSystem is a file, then the size is larger than 0.
            if (!fileSystem.IsDirectory)
            {
                fileSystem.Size = long.Parse(sizeOrDirSegment);
            }

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // Calculate the index of the file name part in the original string.
            int filenameIndex = 0;

            for (int i = 0; i < index; i++)
            {
                // "" represents ' ' in the original string.
                if (segments[i] == string.Empty)
                {
                    filenameIndex += 1;
                }
                else
                {
                    filenameIndex += segments[i].Length + 1;
                }
            }
            // The file name may include many segments because the name can contain ' '.
            fileSystem.Name = fileSystem.OriginalRecordString.Substring(filenameIndex).Trim();

            return(fileSystem);
        }
        /// <summary>
        /// The recordString is like
        /// Directory: drwxrwxrwx   1 owner    group               0 Dec 13 11:25 Folder A
        /// File:      -rwxrwxrwx   1 owner    group               1024 Dec 13 11:25 File B
        /// NOTE: The date segment does not contains year.
        /// </summary>
        static FTPFileSystem ParseUNIXRecordString(string recordString)
        {
            FTPFileSystem fileSystem = new FTPFileSystem();

            fileSystem.OriginalRecordString  = recordString.Trim();
            fileSystem.DirectoryListingStyle = FTPDirectoryListingStyle.UNIX;

            // The segments is like "drwxrwxrwx", "",  "", "1", "owner", "", "", "",
            // "group", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
            // "0", "Dec", "13", "11:25", "Folder", "A".
            string[] segments = fileSystem.OriginalRecordString.Split(' ');

            int index = 0;

            // The permission segment is like "drwxrwxrwx".
            string permissionsegment = segments[index];

            // If the property start with 'd', then it means a directory.
            fileSystem.IsDirectory = permissionsegment[0] == 'd';

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // Skip the directories segment.

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // Skip the owner segment.

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // Skip the group segment.

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // If this fileSystem is a file, then the size is larger than 0.
            fileSystem.Size = long.Parse(segments[index]);

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // The month segment.
            string monthsegment = segments[index];

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // The day segment.
            string daysegment = segments[index];

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // The time segment.
            string timesegment = segments[index];

            fileSystem.ModifiedTime = DateTime.Parse(string.Format("{0} {1} {2} ",
                                                                   timesegment, monthsegment, daysegment));

            // Skip the empty segments.
            while (segments[++index] == string.Empty)
            {
            }

            // Calculate the index of the file name part in the original string.
            int filenameIndex = 0;

            for (int i = 0; i < index; i++)
            {
                // "" represents ' ' in the original string.
                if (segments[i] == string.Empty)
                {
                    filenameIndex += 1;
                }
                else
                {
                    filenameIndex += segments[i].Length + 1;
                }
            }
            // The file name may include many segments because the name can contain ' '.
            fileSystem.Name = fileSystem.OriginalRecordString.Substring(filenameIndex).Trim();

            return(fileSystem);
        }
        /// <summary>
        /// Download a single file from FTP server using WebRequest.
        /// </summary>
        public static async Task <DownloadCompletedEventArgs> DownloadFTPFileAsync(FTPFileSystem item,
                                                                                   StorageFile targetFile, ICredentials credential)
        {
            var result = new DownloadCompletedEventArgs
            {
                RequestFile = item.Url,
                LocalFile   = targetFile,
                Error       = null
            };

            // This request is FtpWebRequest in fact.
            WebRequest request = WebRequest.Create(item.Url);

            if (credential != null)
            {
                request.Credentials = credential;
            }

            request.Proxy = WebRequest.DefaultWebProxy;

            // Set the method to Download File
            request.Method = "RETR";
            try
            {
                // Open the file for write.
                using (IRandomAccessStream fileStream =
                           await targetFile.OpenAsync(FileAccessMode.ReadWrite))
                {
                    // Get response.
                    using (WebResponse response = await request.GetResponseAsync())
                    {
                        // Get response stream.
                        using (Stream responseStream = response.GetResponseStream())
                        {
                            byte[] downloadBuffer = new byte[2048];
                            int    bytesSize      = 0;

                            // Download the file until the download is completed.
                            while (true)
                            {
                                // Read a buffer of data from the stream.
                                bytesSize = responseStream.Read(downloadBuffer, 0,
                                                                downloadBuffer.Length);
                                if (bytesSize == 0)
                                {
                                    break;
                                }

                                // Write buffer to the file.
                                await fileStream.WriteAsync(downloadBuffer.AsBuffer());
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                result.Error = ex;
            }

            return(result);
        }