Пример #1
0
        /// <summary>
        /// Opens the specified file for writing. Please call GetReply() after you have successfully transfered the file to read the "OK" command sent by the server and prevent stale data on the socket.
        /// </summary>
        /// <param name="path">Full or relative path of the file</param>
        /// <param name="type">ASCII/Binary</param>
        /// <returns>A stream for writing to the file on the server</returns>
        /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
        public virtual Stream OpenWrite(string path, FtpDataType type)
        {
            // verify args
            if (path.IsBlank())
            {
                throw new ArgumentException("Required parameter is null or blank.", "path");
            }

            FtpTrace.WriteFunc("OpenWrite", new object[] { path, type });

            FtpClient client     = null;
            FtpDataStream stream = null;
            long length          = 0;

#if !CORE14
            lock (m_lock) {
#endif
            if (m_threadSafeDataChannels)
            {
                client = CloneConnection();
                client.Connect();
                client.SetWorkingDirectory(GetWorkingDirectory());
            }
            else
            {
                client = this;
            }

            client.SetDataType(type);
            length = client.GetFileSize(path);
            stream = client.OpenDataStream(("STOR " + path.GetFtpPath()), 0);

            if (length > 0 && stream != null)
            {
                stream.SetLength(length);
            }
#if !CORE14
        }
#endif

            return(stream);
        }
Пример #2
0
        /// <summary>
        /// Disconnects a data stream
        /// </summary>
        /// <param name="stream">The data stream to close</param>
        internal FtpReply CloseDataStream(FtpDataStream stream)
        {
            FtpTrace.WriteFunc("CloseDataStream");

            FtpReply reply = new FtpReply();

            if (stream == null)
            {
                throw new ArgumentException("The data stream parameter was null");
            }

            lock (m_lock) {
                try {
                    if (IsConnected)
                    {
                        // if the command that required the data connection was
                        // not successful then there will be no reply from
                        // the server, however if the command was successful
                        // the server will send a reply when the data connection
                        // is closed.
                        if (stream.CommandStatus.Type == FtpResponseType.PositivePreliminary)
                        {
                            if (!(reply = GetReply()).Success)
                            {
                                throw new FtpCommandException(reply);
                            }
                        }
                    }
                } finally {
                    // if this is a clone of the original control
                    // connection we should Dispose()
                    if (IsClone)
                    {
                        Disconnect();
                        Dispose();
                    }
                }
            }

            return(reply);
        }
Пример #3
0
        /// <summary>
        /// Opens the specified file for reading
        /// </summary>
        /// <param name="path">The full or relative path of the file</param>
        /// <param name="type">ASCII/Binary</param>
        /// <param name="restart">Resume location</param>
        /// <returns>A stream for reading the file on the server</returns>
        /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
        public virtual Stream OpenRead(string path, FtpDataType type, long restart)
        {
            FtpTrace.WriteFunc("OpenRead", new object[] { path, type, restart });

            FtpClient     client = null;
            FtpDataStream stream = null;
            long          length = 0;

            lock (m_lock) {
                if (m_threadSafeDataChannels)
                {
                    client = CloneConnection();
                    client.Connect();
                    client.SetWorkingDirectory(GetWorkingDirectory());
                }
                else
                {
                    client = this;
                }

                client.SetDataType(type);
                length = client.GetFileSize(path);
                stream = client.OpenDataStream(("RETR " + path.GetFtpPath()), restart);
            }

            if (stream != null)
            {
                if (length > 0)
                {
                    stream.SetLength(length);
                }

                if (restart > 0)
                {
                    stream.SetPosition(restart);
                }
            }

            return(stream);
        }
Пример #4
0
        /// <summary>
        /// Opens the specified file for appending. Please call GetReply() after you have successfully transfered the file to read the "OK" command sent by the server and prevent stale data on the socket.
        /// </summary>
        /// <param name="path">The full or relative path to the file to be opened</param>
        /// <param name="type">ASCII/Binary</param>
        /// <returns>A stream for writing to the file on the server</returns>
        /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
        public virtual Stream OpenAppend(string path, FtpDataType type)
        {
            FtpTrace.WriteFunc("OpenAppend", new object[] { path, type });

            FtpClient client     = null;
            FtpDataStream stream = null;
            long length          = 0;

#if !CORE14
            lock (m_lock) {
#endif
            if (m_threadSafeDataChannels)
            {
                client = CloneConnection();
                client.Connect();
                client.SetWorkingDirectory(GetWorkingDirectory());
            }
            else
            {
                client = this;
            }

            client.SetDataType(type);
            length = client.GetFileSize(path);
            stream = client.OpenDataStream(("APPE " + path.GetFtpPath()), 0);

            if (length > 0 && stream != null)
            {
                stream.SetLength(length);
                stream.SetPosition(length);
            }
#if !CORE14
        }
#endif

            return(stream);
        }
Пример #5
0
        /// <summary>
        /// Connect to the specified host
        /// </summary>
        /// <param name="host">The host to connect to</param>
        /// <param name="port">The port to connect to</param>
        /// <param name="ipVersions">Internet Protocol versions to support during the connection phase</param>
        public void Connect(string host, int port, FtpIpVersion ipVersions)
        {
#if CORE
            IPAddress[] addresses = Dns.GetHostAddressesAsync(host).Result;
#else
            IAsyncResult ar        = null;
            IPAddress[]  addresses = Dns.GetHostAddresses(host);
#endif

            if (ipVersions == 0)
            {
                throw new ArgumentException("The ipVersions parameter must contain at least 1 flag.");
            }

            for (int i = 0; i < addresses.Length; i++)
            {
                // we don't need to do this check unless
                // a particular version of IP has been
                // omitted so we won't.
                if (ipVersions != FtpIpVersion.ANY)
                {
                    switch (addresses[i].AddressFamily)
                    {
                    case AddressFamily.InterNetwork:
                        if ((ipVersions & FtpIpVersion.IPv4) != FtpIpVersion.IPv4)
                        {
#if DEBUG
                            FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped IPV4 address : " + addresses[i].ToString());
#endif
                            continue;
                        }
                        break;

                    case AddressFamily.InterNetworkV6:
                        if ((ipVersions & FtpIpVersion.IPv6) != FtpIpVersion.IPv6)
                        {
#if DEBUG
                            FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped IPV6 address : " + addresses[i].ToString());
#endif
                            continue;
                        }
                        break;
                    }
                }

                if (FtpTrace.LogIP)
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Info, "Connecting to " + addresses[i].ToString() + ":" + port);
                }
                else
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Info, "Connecting to ***:" + port);
                }

                m_socket = new Socket(addresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);
#if CORE
                m_socket.ConnectAsync(addresses[i], port).Wait();
#else
                ar = m_socket.BeginConnect(addresses[i], port, null, null);
                if (!ar.AsyncWaitHandle.WaitOne(m_connectTimeout, true))
                {
                    Close();

                    // check to see if we're out of addresses, and throw a TimeoutException
                    if ((i + 1) == addresses.Length)
                    {
                        throw new TimeoutException("Timed out trying to connect!");
                    }
                }
                else
                {
                    m_socket.EndConnect(ar);
                    // we got a connection, break out
                    // of the loop.
                    break;
                }
#endif
            }

            // make sure that we actually connected to
            // one of the addresses returned from GetHostAddresses()
            if (m_socket == null || !m_socket.Connected)
            {
                Close();
                throw new IOException("Failed to connect to host.");
            }

            m_netStream    = new NetworkStream(m_socket);
            m_lastActivity = DateTime.Now;
        }
Пример #6
0
 /// <summary>
 /// Disposes the stream
 /// </summary>
 public new void Dispose()
 {
     FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Disposing FtpSocketStream...");
     Close();
 }
Пример #7
0
        /// <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());
        }
Пример #8
0
        /// <summary>
        /// Returns information about a file system object. Returns null if the server response can't
        /// be parsed or the server returns a failure completion code. The error for a failure
        /// is logged with FtpTrace. No exception is thrown on error because that would negate
        /// the usefulness of this method for checking for the existence of an object.
        /// </summary>
        /// <param name="path">The path of the file or folder</param>
        /// <param name="dateModified">Get the accurate modified date using another MDTM command</param>
        /// <returns>A FtpListItem object</returns>
        public FtpListItem GetObjectInfo(string path, bool dateModified = false)
        {
            FtpTrace.WriteFunc("GetObjectInfo", new object[] { path, dateModified });

            FtpReply reply;

            string[] res;

            bool supportsMachineList = (Capabilities & FtpCapability.MLSD) == FtpCapability.MLSD;

            FtpListItem result = null;

            if (supportsMachineList)
            {
                // USE MACHINE LISTING TO GET INFO FOR A SINGLE FILE

                if ((reply = Execute("MLST " + path)).Success)
                {
                    res = reply.InfoMessages.Split('\n');
                    if (res.Length > 1)
                    {
                        string info = "";

                        for (int i = 1; i < res.Length; i++)
                        {
                            info += res[i];
                        }

                        result = m_listParser.ParseSingleLine(null, info, m_caps, true);
                    }
                }
                else
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to get object info for path " + path + " with error " + reply.ErrorMessage);
                }
            }
            else
            {
                // USE GETLISTING TO GET ALL FILES IN DIR .. SLOWER BUT AT LEAST IT WORKS

                string        dirPath  = path.GetFtpDirectoryName();
                FtpListItem[] dirItems = GetListing(dirPath);

                foreach (var dirItem in dirItems)
                {
                    if (dirItem.FullName == path)
                    {
                        result = dirItem;
                        break;
                    }
                }

                FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to get object info for path " + path + " since MLST not supported and GetListing() fails to list file/folder.");
            }

            // Get the accurate date modified using another MDTM command
            if (result != null && dateModified && HasFeature(FtpCapability.MDTM))
            {
                result.Modified = GetModifiedTime(path);
            }

            return(result);
        }
Пример #9
0
        /// <summary>
        /// Disconnects from server
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Disposing FtpSocketStream...");

#if !NO_SSL
            if (m_bufStream != null)
            {
                // ensure the last of the buffered bytes are flushed
                // before we close the socket and network stream
                m_bufStream.Flush();
            }
#endif

            if (m_socket != null)
            {
                try {
                    if (m_socket.Connected)
                    {
                        ////
                        // Calling Shutdown() with mono causes an
                        // exception if the remote host closed first
                        //m_socket.Shutdown(SocketShutdown.Both);
#if CORE
                        m_socket.Dispose();
#else
                        m_socket.Close();
#endif
                    }

#if !NET20 && !NET35
                    m_socket.Dispose();
#endif
                } catch (SocketException ex) {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded a SocketException while cleaning up the Socket: " + ex.ToString());
                } finally {
                    m_socket = null;
                }
            }

            if (m_netStream != null)
            {
                try {
                    m_netStream.Dispose();
                } catch (IOException ex) {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded an IOException while cleaning up the NetworkStream: " + ex.ToString());
                } finally {
                    m_netStream = null;
                }
            }

#if !NO_SSL
            if (m_sslStream != null)
            {
                try {
                    m_sslStream.Dispose();
                } catch (IOException ex) {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded an IOException while cleaning up the SslStream: " + ex.ToString());
                } finally {
                    m_sslStream = null;
                }
            }

            if (m_bufStream != null)
            {
                try {
                    m_bufStream.Dispose();
                } catch (IOException ex) {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded an IOException while cleaning up the BufferedStream: " + ex.ToString());
                } finally {
                    m_bufStream = null;
                }
            }
#endif
        }
Пример #10
0
        /// <summary>
        /// Parses LIST format listings
        /// </summary>
        /// <param name="buf">A line from the listing</param>
        /// <param name="capabilities">Server capabilities</param>
        /// <returns>FtpListItem if the item is able to be parsed</returns>
        static FtpListItem ParseUnixList(string buf, FtpCapability capabilities)
        {
            string regex =
                @"(?<permissions>.+)\s+" +
                @"(?<objectcount>\d+)\s+" +
                @"(?<user>.+)\s+" +
                @"(?<group>.+)\s+" +
                @"(?<size>\d+)\s+" +
                @"(?<modify>\w+\s+\d+\s+\d+:\d+|\w+\s+\d+\s+\d+)\s" +
                @"(?<name>.*)$";
            FtpListItem item = new FtpListItem();
            Match       m;

            if (!(m = Regex.Match(buf, regex, RegexOptions.IgnoreCase)).Success)
            {
                return(null);
            }

            // if this field is missing we can't determine
            // what the object is.
            if (m.Groups["permissions"].Value.Length == 0)
            {
                return(null);
            }

            switch (m.Groups["permissions"].Value[0])
            {
            case 'd':
                item.Type = FtpFileSystemObjectType.Directory;
                break;

            case '-':
            case 's':
                item.Type = FtpFileSystemObjectType.File;
                break;

            case 'l':
                item.Type = FtpFileSystemObjectType.Link;
                break;

            default:
                return(null);
            }

            // if we can't determine a file name then
            // we are not considering this a successful parsing operation.
            if (m.Groups["name"].Value.Length < 1)
            {
                return(null);
            }
            item.Name = m.Groups["name"].Value;

            switch (item.Type)
            {
            case FtpFileSystemObjectType.Directory:
                // ignore these...
                if (item.Name == "." || item.Name == "..")
                {
                    return(null);
                }
                break;

            case FtpFileSystemObjectType.Link:
                if (!item.Name.Contains(" -> "))
                {
                    return(null);
                }
                item.LinkTarget = item.Name.Remove(0, item.Name.IndexOf("-> ") + 3);
                item.Name       = item.Name.Remove(item.Name.IndexOf(" -> "));
                break;
            }

            // for date parser testing only
            //capabilities = ~(capabilities & FtpCapability.MDTM);

            ////
            // Ignore the Modify times sent in LIST format for files
            // when the server has support for the MDTM command
            // because they will never be as accurate as what can be had
            // by using the MDTM command. MDTM does not work on directories
            // so if a modify time was parsed from the listing we will try
            // to convert it to a DateTime object and use it for directories.
            ////
            if (((capabilities & FtpCapability.MDTM) != FtpCapability.MDTM || item.Type == FtpFileSystemObjectType.Directory) && m.Groups["modify"].Value.Length > 0)
            {
                item.Modified = m.Groups["modify"].Value.GetFtpDate(DateTimeStyles.AssumeLocal);
                if (item.Modified == DateTime.MinValue)
                {
                    FtpTrace.WriteLine("GetFtpDate() failed on {0}", m.Groups["modify"].Value);
                }
            }
            else
            {
                if (m.Groups["modify"].Value.Length == 0)
                {
                    FtpTrace.WriteLine("RegEx failed to parse modified date from {0}.", buf);
                }
                else if (item.Type == FtpFileSystemObjectType.Directory)
                {
                    FtpTrace.WriteLine("Modified times of directories are ignored in UNIX long listings.");
                }
                else if ((capabilities & FtpCapability.MDTM) == FtpCapability.MDTM)
                {
                    FtpTrace.WriteLine("Ignoring modified date because MDTM feature is present. If you aren't already, pass FtpListOption.Modify or FtpListOption.SizeModify to GetListing() to retrieve the modification time.");
                }
            }

            if (m.Groups["size"].Value.Length > 0)
            {
                long size;

                if (long.TryParse(m.Groups["size"].Value, out size))
                {
                    item.Size = size;
                }
            }

            if (m.Groups["permissions"].Value.Length > 0)
            {
                Match perms = Regex.Match(m.Groups["permissions"].Value,
                                          @"[\w-]{1}(?<owner>[\w-]{3})(?<group>[\w-]{3})(?<others>[\w-]{3})",
                                          RegexOptions.IgnoreCase);

                if (perms.Success)
                {
                    if (perms.Groups["owner"].Value.Length == 3)
                    {
                        if (perms.Groups["owner"].Value[0] == 'r')
                        {
                            item.OwnerPermissions |= FtpPermission.Read;
                        }
                        if (perms.Groups["owner"].Value[1] == 'w')
                        {
                            item.OwnerPermissions |= FtpPermission.Write;
                        }
                        if (perms.Groups["owner"].Value[2] == 'x' || perms.Groups["owner"].Value[2] == 's')
                        {
                            item.OwnerPermissions |= FtpPermission.Execute;
                        }
                        if (perms.Groups["owner"].Value[2] == 's' || perms.Groups["owner"].Value[2] == 'S')
                        {
                            item.SpecialPermissions |= FtpSpecialPermissions.SetUserID;
                        }
                    }

                    if (perms.Groups["group"].Value.Length == 3)
                    {
                        if (perms.Groups["group"].Value[0] == 'r')
                        {
                            item.GroupPermissions |= FtpPermission.Read;
                        }
                        if (perms.Groups["group"].Value[1] == 'w')
                        {
                            item.GroupPermissions |= FtpPermission.Write;
                        }
                        if (perms.Groups["group"].Value[2] == 'x' || perms.Groups["group"].Value[2] == 's')
                        {
                            item.GroupPermissions |= FtpPermission.Execute;
                        }
                        if (perms.Groups["group"].Value[2] == 's' || perms.Groups["group"].Value[2] == 'S')
                        {
                            item.SpecialPermissions |= FtpSpecialPermissions.SetGroupID;
                        }
                    }

                    if (perms.Groups["others"].Value.Length == 3)
                    {
                        if (perms.Groups["others"].Value[0] == 'r')
                        {
                            item.OthersPermissions |= FtpPermission.Read;
                        }
                        if (perms.Groups["others"].Value[1] == 'w')
                        {
                            item.OthersPermissions |= FtpPermission.Write;
                        }
                        if (perms.Groups["others"].Value[2] == 'x' || perms.Groups["others"].Value[2] == 't')
                        {
                            item.OthersPermissions |= FtpPermission.Execute;
                        }
                        if (perms.Groups["others"].Value[2] == 't' || perms.Groups["others"].Value[2] == 'T')
                        {
                            item.SpecialPermissions |= FtpSpecialPermissions.Sticky;
                        }
                    }
                }
            }

            return(item);
        }
Пример #11
0
        /// <summary>
        /// Connect to the specified host
        /// </summary>
        /// <param name="host">The host to connect to</param>
        /// <param name="port">The port to connect to</param>
        /// <param name="ipVersions">Internet Protocol versions to support during the connection phase</param>
        public async Task ConnectAsync(string host, int port, FtpIpVersion ipVersions)
        {
            IPAddress[] addresses = await Dns.GetHostAddressesAsync(host);

            if (ipVersions == 0)
            {
                throw new ArgumentException("The ipVersions parameter must contain at least 1 flag.");
            }

            for (int i = 0; i < addresses.Length; i++)
            {
                // we don't need to do this check unless
                // a particular version of IP has been
                // omitted so we won't.
                if (ipVersions != FtpIpVersion.ANY)
                {
                    switch (addresses[i].AddressFamily)
                    {
                    case AddressFamily.InterNetwork:
                        if ((ipVersions & FtpIpVersion.IPv4) != FtpIpVersion.IPv4)
                        {
#if DEBUG
                            FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped IPV4 address : " + addresses[i].ToString());
#endif
                            continue;
                        }
                        break;

                    case AddressFamily.InterNetworkV6:
                        if ((ipVersions & FtpIpVersion.IPv6) != FtpIpVersion.IPv6)
                        {
#if DEBUG
                            FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped IPV6 address : " + addresses[i].ToString());
#endif
                            continue;
                        }
                        break;
                    }
                }

                if (FtpTrace.LogIP)
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Info, "Connecting to " + addresses[i].ToString() + ":" + port);
                }
                else
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Info, "Connecting to ***:" + port);
                }

                m_socket = new Socket(addresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);

#if CORE
                await m_socket.ConnectAsync(addresses[i], port);

                break;
#else
                var connectResult = m_socket.BeginConnect(addresses[i], port, null, null);
                await Task.Factory.FromAsync(connectResult, m_socket.EndConnect);

                break;
#endif
            }

            // make sure that we actually connected to
            // one of the addresses returned from GetHostAddresses()
            if (m_socket == null || !m_socket.Connected)
            {
                Close();
                throw new IOException("Failed to connect to host.");
            }

            m_netStream    = new NetworkStream(m_socket);
            m_lastActivity = DateTime.Now;
        }
Пример #12
0
        public override void Close()
        {
#endif
            if (m_socket != null)
            {
                try
                {
                    if (m_socket.Connected)
                    {
                        ////
                        // Calling Shutdown() with mono causes an
                        // exception if the remote host closed first
                        //m_socket.Shutdown(SocketShutdown.Both);
#if CORE
                        m_socket.Dispose();
#else
                        m_socket.Close();
#endif
                    }

#if !NET2 && !NET35
                    m_socket.Dispose();
#endif
                }
                catch (SocketException ex)
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded a SocketException while cleaning up the Socket: " + ex.ToString());
                }
                finally
                {
                    m_socket = null;
                }
            }

            if (m_netStream != null)
            {
                try
                {
                    m_netStream.Dispose();
                }
                catch (IOException ex)
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded an IOException while cleaning up the NetworkStream: " + ex.ToString());
                }
                finally
                {
                    m_netStream = null;
                }
            }

#if !NO_SSL
            if (m_sslStream != null)
            {
                try
                {
                    m_sslStream.Dispose();
                }
                catch (IOException ex)
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Caught and discarded an IOException while cleaning up the SslStream: " + ex.ToString());
                }
                finally
                {
                    m_sslStream = null;
                }
            }
#endif

#if CORE
            base.Dispose();
#endif
        }
Пример #13
0
 /// <summary>
 /// Disposes the stream
 /// </summary>
 public new void Dispose()
 {
     FtpTrace.WriteLine("Disposing FtpSocketStream...");
     Close();
 }
Пример #14
0
        /// <summary>
        /// Gets the hash of an object on the server using the currently selected hash algorithm.
        /// </summary>
        /// <remarks>
        /// Supported algorithms, if any, are available in the <see cref="HashAlgorithms"/>
        /// property. You should confirm that it's not equal
        /// to <see cref="FtpHashAlgorithm.NONE"/> before calling this method
        /// otherwise the server trigger a <see cref="FtpCommandException"/>
        /// due to a lack of support for the HASH command. You can
        /// set the algorithm using the <see cref="SetHashAlgorithm"/> method and
        /// you can query the server for the current hash algorithm
        /// using the <see cref="GetHashAlgorithm"/> method.
        ///
        /// This feature is experimental and based on the following draft:
        /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
        /// </remarks>
        /// <param name="path">Full or relative path of the object to compute the hash for.</param>
        /// <returns>The hash of the file.</returns>
        /// <exception cref="FtpCommandException">
        /// Thrown if the <see cref="HashAlgorithms"/> property is <see cref="FtpHashAlgorithm.NONE"/>,
        /// the remote path does not exist, or the command cannot be executed.
        /// </exception>
        /// <exception cref="ArgumentException">Path argument is null</exception>
        /// <exception cref="NotImplementedException">Thrown when an unknown hash algorithm type is returned by the server</exception>
        /// <example><code source="..\Examples\GetHash.cs" lang="cs" /></example>
        public FtpHash GetHash(string path)
        {
            FtpReply reply;
            FtpHash  hash = new FtpHash();
            Match    m;

            if (path == null)
            {
                throw new ArgumentException("GetHash(path) argument can't be null");
            }

#if !CORE14
            lock (m_lock) {
#endif
            if (!(reply = Execute("HASH " + path.GetFtpPath())).Success)
            {
                throw new FtpCommandException(reply);
            }
#if !CORE14
        }
#endif

            // Current draft says the server should return this:
            // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext
            if (!(m = Regex.Match(reply.Message,
                                  @"(?<algorithm>.+)\s" +
                                  @"(?<bytestart>\d+)-(?<byteend>\d+)\s" +
                                  @"(?<hash>.+)\s" +
                                  @"(?<filename>.+)")).Success)
            {
                // Current version of FileZilla returns this:
                // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf
                m = Regex.Match(reply.Message, @"(?<algorithm>.+)\s(?<hash>.+)\s");
            }

            if (m != null && m.Success)
            {
                switch (m.Groups["algorithm"].Value)
                {
                case "SHA-1":
                    hash.Algorithm = FtpHashAlgorithm.SHA1;
                    break;

                case "SHA-256":
                    hash.Algorithm = FtpHashAlgorithm.SHA256;
                    break;

                case "SHA-512":
                    hash.Algorithm = FtpHashAlgorithm.SHA512;
                    break;

                case "MD5":
                    hash.Algorithm = FtpHashAlgorithm.MD5;
                    break;

                default:
                    throw new NotImplementedException("Unknown hash algorithm: " + m.Groups["algorithm"].Value);
                }

                hash.Value = m.Groups["hash"].Value;
            }
            else
            {
                FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse hash from: " + reply.Message);
            }

            return(hash);
        }
Пример #15
0
        /// <summary>
        /// Opens the specified type of active data stream
        /// </summary>
        /// <param name="type">Type of passive data stream to open</param>
        /// <param name="command">The command to execute that requires a data stream</param>
        /// <param name="restart">Restart location in bytes for file transfer</param>
        /// <returns>A data stream ready to be used</returns>
        FtpDataStream OpenActiveDataStream(FtpDataConnectionType type, string command, long restart)
        {
            FtpTrace.WriteFunc("OpenActiveDataStream", new object[] { type, command, restart });

            FtpDataStream stream = new FtpDataStream(this);
            FtpReply      reply;

#if !CORE
            IAsyncResult ar;
#endif

            if (m_stream == null)
            {
                throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open an active data stream.");
            }

            if (m_ActivePorts == null || !m_ActivePorts.Any())
            {
                // Use random port
                stream.Listen(m_stream.LocalEndPoint.Address, 0);
            }
            else
            {
                var success = false;
                // Use one of the specified ports
                foreach (var port in m_ActivePorts)
                {
                    try {
                        stream.Listen(m_stream.LocalEndPoint.Address, port);
                        success = true;
                    } catch (SocketException se) {
#if NETFX
                        // Already in use
                        if (se.ErrorCode != 10048)
                        {
                            throw;
                        }
#else
                        throw;
#endif
                    }
                }

                // No usable port found
                if (!success)
                {
                    throw new Exception("No valid active data port available!");
                }
            }
#if !CORE
            ar = stream.BeginAccept(null, null);
#endif

            if (type == FtpDataConnectionType.EPRT || type == FtpDataConnectionType.AutoActive)
            {
                int ipver = 0;

                switch (stream.LocalEndPoint.AddressFamily)
                {
                case System.Net.Sockets.AddressFamily.InterNetwork:
                    ipver = 1;                             // IPv4
                    break;

                case System.Net.Sockets.AddressFamily.InterNetworkV6:
                    ipver = 2;                             // IPv6
                    break;

                default:
                    throw new InvalidOperationException("The IP protocol being used is not supported.");
                }

                if (!(reply = Execute("EPRT |" + ipver + "|" + GetLocalAddress(stream.LocalEndPoint.Address) + "|" + stream.LocalEndPoint.Port + "|")).Success)
                {
                    // if we're connected with IPv4 and the data channel type is AutoActive then try to fall back to the PORT command
                    if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoActive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        stream.ControlConnection = null;                         // we don't want this failed EPRT attempt to close our control connection when the stream is closed so clear out the reference.
                        stream.Close();
                        return(OpenActiveDataStream(FtpDataConnectionType.PORT, command, restart));
                    }
                    else
                    {
                        stream.Close();
                        throw new FtpCommandException(reply);
                    }
                }
            }
            else
            {
                if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    throw new FtpException("Only IPv4 is supported by the PORT command. Use EPRT instead.");
                }

                if (!(reply = Execute("PORT " +
                                      GetLocalAddress(stream.LocalEndPoint.Address).Replace('.', ',') + "," +
                                      stream.LocalEndPoint.Port / 256 + "," +
                                      stream.LocalEndPoint.Port % 256)).Success)
                {
                    stream.Close();
                    throw new FtpCommandException(reply);
                }
            }

            if (restart > 0)
            {
                if (!(reply = Execute("REST " + restart)).Success)
                {
                    throw new FtpCommandException(reply);
                }
            }

            if (!(reply = Execute(command)).Success)
            {
                stream.Close();
                throw new FtpCommandException(reply);
            }

            // the command status is used to determine
            // if a reply needs to be read from the server
            // when the stream is closed so always set it
            // otherwise things can get out of sync.
            stream.CommandStatus = reply;

#if CORE
            stream.AcceptAsync().Wait();
#else
            ar.AsyncWaitHandle.WaitOne(m_dataConnectionConnectTimeout);
            if (!ar.IsCompleted)
            {
                stream.Close();
                throw new TimeoutException("Timed out waiting for the server to connect to the active data socket.");
            }

            stream.EndAccept(ar);
#endif

#if !NO_SSL
            if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
            {
                stream.ActivateEncryption(m_host,
                                          this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
                                          m_SslProtocols);
            }
#endif

            stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive);
            stream.ReadTimeout = m_dataConnectionReadTimeout;

            return(stream);
        }
Пример #16
0
        /// <summary>
        /// Parses a line from a file listing using the first successful match in the Parsers collection.
        /// </summary>
        /// <param name="path">The source path of the file listing</param>
        /// <param name="buf">A line from the file listing</param>
        /// <param name="capabilities">Server capabilities</param>
        /// <returns>A FtpListItem object representing the parsed line, null if the line was
        /// unable to be parsed. If you have encountered an unsupported list type add a parser
        /// to the public static Parsers collection of FtpListItem.</returns>
        public static FtpListItem Parse(string path, string buf, FtpCapability capabilities)
        {
            if (buf != null && buf.Length > 0)
            {
                FtpListItem item;

                foreach (Parser parser in Parsers)
                {
                    if ((item = parser(buf, capabilities)) != null)
                    {
                        // if this is a vax/openvms file listing
                        // there are no slashes in the path name
                        if (parser == (new Parser(ParseVaxList)))
                        {
                            item.FullName = path + item.Name;
                        }
                        else
                        {
                            FtpTrace.WriteLine(item.Name);

                            // remove globbing/wildcard from path
                            if (path.GetFtpFileName().Contains("*"))
                            {
                                path = path.GetFtpDirectoryName();
                            }

                            if (item.Name != null)
                            {
                                // absolute path? then ignore the path input to this method.
                                if (item.Name.StartsWith("/") || item.Name.StartsWith("./") || item.Name.StartsWith("../"))
                                {
                                    item.FullName = item.Name;
                                    item.Name     = item.Name.GetFtpFileName();
                                }
                                else if (path != null)
                                {
                                    item.FullName = path.GetFtpPath(item.Name); //.GetFtpPathWithoutGlob();
                                }
                                else
                                {
                                    FtpTrace.WriteLine("Couldn't determine the full path of this object:{0}{1}",
                                                       Environment.NewLine, item.ToString());
                                }
                            }


                            // if a link target is set and it doesn't include an absolute path
                            // then try to resolve it.
                            if (item.LinkTarget != null && !item.LinkTarget.StartsWith("/"))
                            {
                                if (item.LinkTarget.StartsWith("./"))
                                {
                                    item.LinkTarget = path.GetFtpPath(item.LinkTarget.Remove(0, 2));
                                }
                                else
                                {
                                    item.LinkTarget = path.GetFtpPath(item.LinkTarget);
                                }
                            }
                        }

                        item.Input = buf;
                        return(item);
                    }
                }
            }

            return(null);
        }
Пример #17
0
        /// <summary>
        /// Opens the specified type of passive data stream
        /// </summary>
        /// <param name="type">Type of passive data stream to open</param>
        /// <param name="command">The command to execute that requires a data stream</param>
        /// <param name="restart">Restart location in bytes for file transfer</param>
        /// <returns>A data stream ready to be used</returns>
        FtpDataStream OpenPassiveDataStream(FtpDataConnectionType type, string command, long restart)
        {
            FtpTrace.WriteFunc("OpenPassiveDataStream", new object[] { type, command, restart });

            FtpDataStream stream = null;
            FtpReply      reply;
            Match         m;
            string        host = null;
            int           port = 0;

            if (m_stream == null)
            {
                throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open a passive data stream.");
            }

            if (type == FtpDataConnectionType.EPSV || type == FtpDataConnectionType.AutoPassive)
            {
                if (!(reply = Execute("EPSV")).Success)
                {
                    // if we're connected with IPv4 and data channel type is AutoPassive then fallback to IPv4
                    if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoPassive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        return(OpenPassiveDataStream(FtpDataConnectionType.PASV, command, restart));
                    }
                    throw new FtpCommandException(reply);
                }

                m = Regex.Match(reply.Message, @"\(\|\|\|(?<port>\d+)\|\)");
                if (!m.Success)
                {
                    throw new FtpException("Failed to get the EPSV port from: " + reply.Message);
                }

                host = m_host;
                port = int.Parse(m.Groups["port"].Value);
            }
            else
            {
                if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    throw new FtpException("Only IPv4 is supported by the PASV command. Use EPSV instead.");
                }

                if (!(reply = Execute("PASV")).Success)
                {
                    throw new FtpCommandException(reply);
                }

                m = Regex.Match(reply.Message, @"(?<quad1>\d+)," + @"(?<quad2>\d+)," + @"(?<quad3>\d+)," + @"(?<quad4>\d+)," + @"(?<port1>\d+)," + @"(?<port2>\d+)");

                if (!m.Success || m.Groups.Count != 7)
                {
                    throw new FtpException(("Malformed PASV response: " + reply.Message));
                }

                // PASVEX mode ignores the host supplied in the PASV response
                if (type == FtpDataConnectionType.PASVEX)
                {
                    host = m_host;
                }
                else
                {
                    host = (m.Groups["quad1"].Value + "." + m.Groups["quad2"].Value + "." + m.Groups["quad3"].Value + "." + m.Groups["quad4"].Value);
                }

                port = (int.Parse(m.Groups["port1"].Value) << 8) + int.Parse(m.Groups["port2"].Value);
            }

            stream = new FtpDataStream(this);
            stream.ConnectTimeout = DataConnectionConnectTimeout;
            stream.ReadTimeout    = DataConnectionReadTimeout;
            Connect(stream, host, port, InternetProtocolVersions);
            stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive);

            if (restart > 0)
            {
                if (!(reply = Execute("REST " + restart)).Success)
                {
                    throw new FtpCommandException(reply);
                }
            }

            if (!(reply = Execute(command)).Success)
            {
                stream.Close();
                throw new FtpCommandException(reply);
            }

            // the command status is used to determine
            // if a reply needs to be read from the server
            // when the stream is closed so always set it
            // otherwise things can get out of sync.
            stream.CommandStatus = reply;

#if !NO_SSL
            // this needs to take place after the command is executed
            if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
            {
                stream.ActivateEncryption(m_host,
                                          this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
                                          m_SslProtocols);
            }
#endif

            return(stream);
        }
        /// <summary>
        /// Transfers a file from the source FTP Server to the destination FTP Server via the FXP protocol asynchronously.
        /// </summary>
        private async Task <bool> TransferFileFXPInternalAsync(string sourcePath, FtpClient remoteClient, string remotePath, bool createRemoteDir, FtpRemoteExists existsMode,
                                                               IProgress <FtpProgress> progress, CancellationToken token, FtpProgress metaProgress)
        {
            FtpReply reply;
            long     offset     = 0;
            bool     fileExists = false;
            long     fileSize   = 0;

            var ftpFxpSession = await OpenPassiveFXPConnectionAsync(remoteClient, progress != null, token);

            if (ftpFxpSession != null)
            {
                try {
                    ftpFxpSession.SourceServer.ReadTimeout = (int)TimeSpan.FromMinutes(30.0).TotalMilliseconds;
                    ftpFxpSession.TargetServer.ReadTimeout = (int)TimeSpan.FromMinutes(30.0).TotalMilliseconds;


                    // check if the file exists, and skip, overwrite or append
                    if (existsMode == FtpRemoteExists.AppendNoCheck)
                    {
                        offset = await remoteClient.GetFileSizeAsync(remotePath, token);

                        if (offset == -1)
                        {
                            offset = 0;                             // start from the beginning
                        }
                    }
                    else
                    {
                        fileExists = await remoteClient.FileExistsAsync(remotePath, token);

                        switch (existsMode)
                        {
                        case FtpRemoteExists.Skip:

                            if (fileExists)
                            {
                                LogStatus(FtpTraceLevel.Info, "Skip is selected => Destination file exists => skipping");

                                //Fix #413 - progress callback isn't called if the file has already been uploaded to the server
                                //send progress reports
                                if (progress != null)
                                {
                                    progress.Report(new FtpProgress(100.0, 0, 0, TimeSpan.FromSeconds(0), sourcePath, remotePath, metaProgress));
                                }

                                return(true);
                            }

                            break;

                        case FtpRemoteExists.Overwrite:

                            if (fileExists)
                            {
                                await remoteClient.DeleteFileAsync(remotePath, token);
                            }

                            break;

                        case FtpRemoteExists.Append:

                            if (fileExists)
                            {
                                offset = await remoteClient.GetFileSizeAsync(remotePath, token);

                                if (offset == -1)
                                {
                                    offset = 0;                                             // start from the beginning
                                }
                            }

                            break;
                        }
                    }

                    fileSize = await GetFileSizeAsync(sourcePath, token);

                    // ensure the remote dir exists .. only if the file does not already exist!
                    if (createRemoteDir && !fileExists)
                    {
                        var dirname = remotePath.GetFtpDirectoryName();
                        if (!await remoteClient.DirectoryExistsAsync(dirname, token))
                        {
                            await remoteClient.CreateDirectoryAsync(dirname, token);
                        }
                    }

                    if (offset == 0 && existsMode != FtpRemoteExists.AppendNoCheck)
                    {
                        // send command to tell the source server to 'send' the file to the destination server
                        if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"RETR {sourcePath}", token)).Success)
                        {
                            throw new FtpCommandException(reply);
                        }

                        //Instruct destination server to store the file
                        if (!(reply = await ftpFxpSession.TargetServer.ExecuteAsync($"STOR {remotePath}", token)).Success)
                        {
                            throw new FtpCommandException(reply);
                        }
                    }
                    else
                    {
                        //tell source server to restart / resume
                        if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"REST {offset}", token)).Success)
                        {
                            throw new FtpCommandException(reply);
                        }

                        // send command to tell the source server to 'send' the file to the destination server
                        if (!(reply = await ftpFxpSession.SourceServer.ExecuteAsync($"RETR {sourcePath}", token)).Success)
                        {
                            throw new FtpCommandException(reply);
                        }

                        //Instruct destination server to append the file
                        if (!(reply = await ftpFxpSession.TargetServer.ExecuteAsync($"APPE {remotePath}", token)).Success)
                        {
                            throw new FtpCommandException(reply);
                        }
                    }

                    var  transferStarted = DateTime.Now;
                    long lastSize        = 0;


                    var sourceFXPTransferReply      = ftpFxpSession.SourceServer.GetReplyAsync(token);
                    var destinationFXPTransferReply = ftpFxpSession.TargetServer.GetReplyAsync(token);

                    // while the transfer is not complete
                    while (!sourceFXPTransferReply.IsCompleted || !destinationFXPTransferReply.IsCompleted)
                    {
                        // send progress reports every 1 second
                        if (ftpFxpSession.ProgressServer != null)
                        {
                            // send progress reports
                            if (progress != null && fileSize != -1)
                            {
                                offset = await ftpFxpSession.ProgressServer.GetFileSizeAsync(remotePath, token);

                                if (offset != -1 && lastSize <= offset)
                                {
                                    long bytesProcessed = offset - lastSize;
                                    lastSize = offset;
                                    ReportProgress(progress, fileSize, offset, bytesProcessed, DateTime.Now - transferStarted, sourcePath, remotePath, metaProgress);
                                }
                            }
                        }

                        await Task.Delay(FXPProgressInterval);
                    }

                    FtpTrace.WriteLine(FtpTraceLevel.Info, $"FXP transfer of file {sourcePath} has completed");

                    await NoopAsync(token);

                    await remoteClient.NoopAsync(token);

                    ftpFxpSession.Dispose();

                    return(true);
                }

                // Fix: catch all exceptions and dispose off the FTP clients if one occurs
                catch (Exception ex) {
                    ftpFxpSession.Dispose();
                    throw ex;
                }
            }
            else
            {
                FtpTrace.WriteLine(FtpTraceLevel.Error, "Failed to open FXP passive Connection");
                return(false);
            }
        }
Пример #19
0
 /// <summary>
 /// Disposes the stream
 /// </summary>
 public new void Dispose()
 {
     FtpTrace.WriteLine(FtpTraceLevel.Debug, "Disposing FtpSocketStream...");
     Close();
 }